C#でJSONのパース
こんばんはおはようございますこんにちは
今回はC#でJSONのパースをしてみたいと思います。
neueccさんの作られたDynamicJsonという素晴らしいライブラリもありますが、ここでは標準ライブラリを使うことにします。
標準ライブラリでJSONのパースをするには、DataContractJsonSerializerクラスを使います。
なお、.NET Framework 4.0以上が必須です。
まずは使ってみる
使い方はとても簡単で、取得したいオブジェクトの型でDataContractJsonSerializerのインスタンスを初期化して、文字列をMemoryStream経由で読み込ませるだけです。
using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Text; [DataContract] class Data { [DataMember] public int a; [DataMember] public double d; [DataMember] public string s; } class Program { static void Main() { var json = @"{""a"":123, ""s"":""test!"", ""pi"":3.14}"; var serializer = new DataContractJsonSerializer(typeof(Data)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json))) { var data = (Data)serializer.ReadObject(ms); Console.WriteLine(data.a); Console.WriteLine(data.d); Console.WriteLine(data.s); } } }
このように、JSONデータの中に存在しないフィールド・プロパティがあるなどJSONデータとオブジェクトが完全には一致していなくても、デフォルト値を入れるなど適当に処理してくれます。
また、読み込ませたいオブジェクトにはDataContractAttributeを、読み込むフィールド・プロパティにはDataMemberAttributeをつけないといけないことに注意しましょう。DataMemberAttributeをつけていないフィールドやプロパティはJSONから読み込まれません。
DataMemberAttributeさえつけていれば、publicだけではなく例えばprivateなフィールド・プロパティでも読み込んでくれます。
Dictionaryの読み込み
このDataContractJsonSerializerは、組み込み型でもユーザ定義型でもだいたい読み込んでくれます。
ただし、データをDictionaryとして読み込みたいときは少しコードを変えないといけません。
具体的に何をするかというと、DataContractJsonSerializerSettingsのインスタンスを作って、UseSimpleDictionaryFormatプロパティをtrueにして、DataContractJsonSerializerのインスタンスを作ります。
using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Text; [DataContract] class Data { [DataMember] public Dictionary<string, double> xs; } class Program { static void Main() { var json = @"{""xs"":{""1"":1, ""e"":2.718, ""pi"":3.14}}"; var settings = new DataContractJsonSerializerSettings(); settings.UseSimpleDictionaryFormat = true; var serializer = new DataContractJsonSerializer(typeof(Data), settings); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json))) { var data = (Data)serializer.ReadObject(ms); foreach (var x in data.xs) { Console.WriteLine("{0}\t{1}", x.Key, x.Value); } } } }
読み込み時に自動的にデータを変換する
TwitterAPIを叩いたときに返ってくるJSONデータの中には、created_atプロパティのように「中身は実質的にDateTimeだけど、JSONに載せる都合上stringで送られてくる」ものも存在します(JSONに載せられるデータ型は実質的に、数値と文字列とそれらの配列だけです)。
中身は実質的にDateTimeなので、JSONを読み込むと自動的にstringからDateTimeに変換してほしいものですが、実際にはそんなに簡単にはいきません。
というのも、Twitterから送られてくるcreated_atプロパティの中のフォーマットがDateTimeのデフォルトの文字列解釈のフォーマットと異なるため、そのままではちゃんと読み込んでくれないのですね。
そこで、DataMemberAttributeをつけたものしか読み込まないという仕様をうまく使って、データ読み込み時に自動的にデータ変換するようにします。
using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Text; [DataContract] class Data { [DataMember(Name = "created_at")] private string created_at_str_prop { get { return created_at_str_field; } set { created_at_field = DateTime.ParseExact(value, "ddd MMM dd HH:mm:ss zzz yyyy", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None); created_at_str_field = value; } } private string created_at_str_field; public DateTime created_at { get { return created_at_field; } set { created_at_str_field = value.ToString("ddd MMM dd HH:mm:ss zzz yyyy", DateTimeFormatInfo.InvariantInfo); created_at_field = value; } } private DateTime created_at_field; } class Program { static void Main() { var json = @"{""created_at"":""Wed Sep 11 20:01:35 +0000 2013""}"; var serializer = new DataContractJsonSerializer(typeof(Data)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json))) { var data = (Data)serializer.ReadObject(ms); Console.WriteLine(data.created_at); } } }
JSONデータを読み込むときに、DataMemberAttiributeがついているcreated_at_str_propプロパティのsetterが呼び出されるので、その中でデータを変換しているわけです。
Data.created_atプロパティ以外はクラスの外からは見えないので、あたかもJSONを読み込むと自動的にstringをDateTimeに変換しているように見えるわけですね。
また、今まで特に触れていませんでしたが、DataMemberAttributeをつけるときに、Name = "hogehoge"
と指定することで、JSONデータ中のフィールド名を指定できます。
最後に
これでDataContractJsonSerializerを使ってJSON形式のデータを読み込む方法の紹介がざっと終わったわけですが、書き込むときもReadObjectメソッドではなくWriteObjectメソッドを使って同じようにすればできるみたいです。