気象情報XMLを処理して天気予報を表示する
それでは、気象情報のXMLを処理していきましょう。
気象情報XMLのURLを取得する
まずは、Atomフィードの処理です。Atomフィードといっても、XMLファイルなので、やることはXMLファイルの処理と同様となります。
XMLファイルを扱う方法として、すべて読み込んでから処理する方法(DOM)と、先頭から読み込んで要素毎に処理する方法(SAX)、という2つの方法が一般的です。ただし、.NETでは、LINQ(LINQ to XML)という機能も利用できます。LINQでは、データベースのSQL的な問い合わせ構文で、コレクションやXMLファイルなどを扱えます。
今回は、C#言語内でXMLが操作できるLINQ to XMLを利用して、Atomフィードを処理するようにしました。指定された府県コードの天気予報XMLファイルのURLを抽出するコードは、次のようになります。
// 指定された府県コードの天気予報XMLファイルのURLを取得する public string GetUrl(string code) { // Atomフィードの読み込み(1) var xml = XElement.Load(AtomフィードのURL); // 名前空間の取得(2) var nspace = xml.Name.Namespace; // LINQ(クエリ式)で該当のURLを抽出する(3) var urls = from item in xml.Descendants(nspace + "id") where item.Value.Contains("VPFD51_" + code) orderby item.Value descending select item.Value; // データがあれば先頭のデータを返す(4) return urls.Any() ? urls.First() : null; }
LINQ to XMLで提供されるクラスは、System.Xml.Linq名前空間で定義されています。ここでは、XElementクラスを使って、XMLファイルを読み込んでいます(1)。XElementクラスは、LINQ to XMLのベースとなるクラスのひとつで、XML要素の作成や変更、追加、削除などが行えます。XElementクラスのLoadメソッドは、XMLファイルのルート要素を示すXElementオブジェクトを返します。またLoadメソッドは、ローカルのファイルだけでなく、ここでの例のように外部サイトにあるファイルも指定可能です。
次に、XML名前空間を取得します(2)。Atomフィードのルート要素である<feed>要素には、xmlns属性で、XML名前空間が宣言されています。これが定義されている場合、LINQ to XMLで要素を指定する際には、要素名にXML名前空間を付加する必要があります。
(3)のコードが、LINQを使ったコードになります。今回は、LINQのクエリ式と呼ばれる記法としています。fromキーワードで、列挙型などのシーケンスデータを指定し、inキーワードの後に、そのオブジェクトから取り出した値を格納する変数(ここではitem)を指定します。
変数itemも、XElementオブジェクトになります。XElementクラスのDescendantsメソッドは、子孫要素から、指定された名前の要素をすべて列挙します(戻り値はIEnumerableオブジェクトとなります)。(3)のコードでは、ルート要素以下のすべての要素から、idという名前の要素を取得することになります。
次のwhereキーワードは、抽出条件の指定です。ここでは、<id>要素の値に「"VPFD51_" + 指定の府県コード」という文字列が含まれるもの、としています。VPFD51は、天気予報を示すコードです。<id>要素の値は、気象情報XMLのURLとなっていて、またURLには、種別を示すコードと府県コードが含まれています。
orderbyキーワードは、ソートの指定です。ここでは、ソートのキーとして要素の値(item.Valueプロパティ)を指定し、descendingをつけて降順としています。
最後のselectキーワードでは、クエリ式の戻り値であるIEnumerableオブジェクトの型を指定します。ここでは、XElementクラスのValueプロパティを指定していますので、戻り値は、IEnumerable<string>となります。ちなみに、単にitemだけなら、IEnumerable<XElement>となります。
戻り値のIEnumerable<string>には、値が複数含まれる可能性がありますので、Firstメソッドで先頭の値のみを取得しています(4)。
天気予報XMLファイルをHTMLに変換する
天気予報XMLファイルのURLが取得できるようになったので、次は、そのXMLを処理しましょう。ただ、天気予報XMLはかなり複雑でわかりにくいため、今回は、XSLTスタイルシートを用いて変換するようにしました。
XSLT(eXtensible Stylesheet Language Transformations)とは、XMLドキュメントをHTMLドキュメントやプレーンテキストに変換するための言語で、XSLTスタイルシートは、その変換処理を記述したファイルになります。気象庁のサイトには、参考資料として、天気予報XMLをHTMLに変換できるXSLTスタイルシートが公開されています。今回のアプリでは、サイトで公開されているXSLTスタイルシート(190501_yoho.xsl)をダウンロードして利用しました。なお、190501_yoho.xslは、~.Serverプロジェクト直下にコピーしておきます。
XSLTスタイルシートでの変換は、.NET環境では、XslCompiledTransformクラスを利用すれば簡単に行うことができます。それでは、JmaControllerクラスに、指定した府県コードの天気予報HTMLを返すGetYohoメソッドを追加しましょう。
// オブジェクト初期化(1) readonly XslCompiledTransform xslt = new(); public JmaController() { // XSLTファイル読み込み(2) xslt.Load("190501_yoho.xsl"); } Route("yoho/{code}")] public ContentResult GetYoho(string code) { // 指定の府県コードに該当するURLを取得する var url = GetUrl(code); if (url != null) { // XmlWriterの初期化(3) var html = new StringBuilder(); using var writer = XmlWriter.Create(html); // XSLT変換処理(4) xslt.Transform(url, writer); // HTMLを返す(5) return base.Content(html.ToString(), "text/html"); } return base.Content("<html><head><meta charset=\"utf-8\" /></head><body>予報がありません</body></html>", "text/html"); }
最初に、クラスのメンバとして定義したXslCompiledTransformオブジェクトを初期化します(1)。C#9.0から、このように、new型名()の型名を省略した書き方ができるようになりました。
XslCompiledTransformクラスの基本的な使い方は、LoadメソッドでXSLTスタイルシートを読み込み(2)、Transformメソッドで変換する(4)、という手順になります。Transformメソッドで変換するXMLファイルは、あらかじめ読み込む必要はなく、URLを第1引数に指定するだけで変換できます。Transformメソッドの第2引数には、XmlWriterオブジェクトを指定します。変換結果をファイルに出力する場合は、出力ファイル名の指定も可能です。
XmlWriterクラスは、XMLを作成するためのライターオブジェクトで、静的メソッドのCreateメソッドを使って初期化しておきます(3)。なお、XmlWriterオブジェクトは最後にクローズ処理が必要なため、usingステートメントを使って、自動的にCloseメソッドが呼び出されるようにしています。
変換結果は、親クラスのContentメソッドを用いてHTMLとして返します(5)。GetYohoメソッドの戻り値をHTMLとして認識させるために、メソッドの戻り値をContentResultオブジェクトとしています。ContentResultオブジェクトでは、コンテンツタイプ(Content-Type)を指定できるため、HTMLコンテンツとして返すことができます。
天気予報表示ページ
最後に、天気予報表示を表示する処理を追加します。といっても、次のようにインラインフレームの<iframe>タグを追加するだけです。
~略~ @if (codes == null) { <p><em>Loading...</em></p> } else { <select @bind="selectCode" class="form-control"> @foreach (var c in codes) { <option value="@c.Code">@c.Name</option> } </select> <br /> <iframe src="api/jma/yoho/@selectCode" width="100%" height="800" frameborder="0"></iframe> } ~略~
インラインフレームのsrc属性に、先ほど追加したAPIのURLを指定しています。これだけで、天気予報のコンテンツが表示されます。また、セレクトボックスの値が変更されるたびに、自動的にインラインフレームの表示が更新されます。
なお、サンプルのアプリでは、NavMenu.razorを少し変更して、メニューから天気予報ページを表示できるようにしています。
~略~ <li class="nav-item px-3"> <NavLink class="nav-link" href="tenki"> <span class="oi oi-plus" aria-hidden="true"></span> 天気予報 </NavLink> </li> ~略~