気象情報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>
~略~
