C#(VS2008)の標準機能のみでJSONを読み込む
Microsoft Visual C# 2008 の標準機能のみでJSONを読み込む方法について調べた。
最終目標は C# で Web API を叩いて得られた JSON を読み込むこと。
以下のような JSON を読み込むものとする。
お天気Webサービス仕様 - Weather Hacks - livedoor 天気情報
Visual Studio、というか .NET Framework はバージョンによって大きく機能が異なるため、 まず利用する .NET Framework のバージョンを押さえておく必要がある。
Visual Studio 2008に含まれる.NET Frameworkのバージョンは「3.5」。 この場合、DataContractJsonSerializerを使うのが一般的なようだ。
ちなみに、.NET Framework 4.0 以降で実装された dynamic を使った、DynamicJson を用いるともっと楽にできるが、今回は題記の条件により見送る。
DataContractJsonSerializerを使う準備
参照の追加
Visual Studio で、プロジェクトの参照設定を右クリック→参照の追加で、 以下を追加する。
System.Runtime.Serialization System.ServiceModel.Web
名前空間を定義
using System.Runtime.Serialization; using System.Runtime.Serialization.Json;
ここまでのはまりポイント
最初、System.Runtime.Serialization.Json を用いるため、 System.Runtime.Serialization.Json が参照の追加にあると思った。
しかし、実際には System.Runtime.Serialization.Json は参照追加の項目には存在せず、 System.ServiceModel.Web を参照追加するのが正しい。
ちなみに System.ServiceModel.Web を参照追加せず、 using System.Runtime.Serialization.Json した場合、以下のエラーが出る。
このあたりは非常にややこしいので注意。
参考: JSONとクラスのマッピング - Paradigm Shift Design
読み込む JSON に応じたクラスを用意する
まず、読み込む JSON に応じたクラスを用意する必要がある。 今回は以下にちゃんとしたドキュメントがあるので、それに従ってクラスを作る。
お天気Webサービス仕様 - Weather Hacks - livedoor 天気情報
最初にデータ全体のクラスを用意する。 クラスに[DataContract]をつけて定義するのがポイント。
[DataContract] public class Weather { }
続いて、メンバであるが、今回読み込むのは入れ子になっていて結構ややこしい。 まずは入れ子になっていないものから考える。
例えば、title, link, publicTime というプロパティは以下のように定義する。 メンバに[DataMember]をつけて定義するのがポイント。
[DataContract] public class Weather { [DataMember] public String title { get; set; } [DataMember] public String link { get; set; } [DataMember] public String publicTime { get; set; } }
入れ子になっているメンバはどうするかと言うと、 さらに[DataContract]をつけたクラスを定義する。
例えば、location だと以下のようになる。
[DataContract] public class Weather { [DataMember] public Location location { get; set; } //... [DataContract] public class Location { [DataMember] public string area { get; set; } [DataMember] public string pref { get; set; } [DataMember] public string city { get; set; } } //... }
プロパティが配列になっている場合は、そのまま配列で定義すれば良い。
[DataContract] public class Weather { //... [DataMember] public Forecasts[] forecasts { get; set; } //... }
ちなみに、ここでは単純化のためにすべて string 型で定義している。
プリミティブ型(int、floatなど)を用いた場合、null があると面倒なので 必要に応じて後でキャストする方針。
このような感じで全部のプロパティを定義していく。
ここでのはまりポイント
pinpointLocation ではなく、pinpointLocations
copyright の provider が実は配列
temperature は max, min それぞれが celsius, fahrenheit というプロパティを持っている
ここでは、C# というより Livedoor Weather Web Service のドキュメントに惑わされた。
実際の読み出し
using System.IO; using System.Net; // (省略) // Livedoor Weather Web Service の基本URL + 久留米のID(400040) string url = "http://weather.livedoor.com/forecast/webservice/json/v1?city=400040"; // リクエストを作成 HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); // レスポンスを取得 HttpWebResponse webres = (HttpWebResponse)req.GetResponse(); // ストリームを取得 Stream st = webres.GetResponseStream(); // 取得したデータを格納するための変数を用意 Weather weather; using (st) { // DataContractJsonSerializer のインスタンスを作成 var serializer = new DataContractJsonSerializer(typeof(Weather)); // JSON を読み込む weather = (Weather)serializer.ReadObject(st); }
これだけで取得したデータがちゃんと C# のクラスに格納される。 最初の定義が面倒だけど、かなり便利。
Visual Studio でブレークポイント貼って、 ウォッチ式で weather にちゃんと格納されているのを確認した。
ソースは整えて後日アップしようと考え中。