Json.NET (Newtonsoft.Json) の基本的な使い方

意外と困っている人が多いので、今さらだが、まとめ。

JToken を使った基本操作

まず大前提として、使用するオブジェクト構成の理解が超重要。
構成は非常に簡単だ。オブジェクト (例: { "firstname" : "Subaru", "surname" : "Kokubun" })JObject、配列 (例 : [ { "surname" : "Kokubun" }, { "surname" : "Matsuzaki" }, ... ] ) は JArray、文字列や数値などのプリミティブは JValue、そして、これらに共通の親 (継承元) が JToken だ。(この構成を理解していないと、このあとの文書は理解できない !)

例えば、HTTP (REST API) を呼び出して、

{
  "results": [
    {
      "id": 1,
      "name": "pc",
      "price": 100000
    },
    {
      "id": 2,
      "name": "mouse",
      "price": 5000
    },
    {
      "id": 3,
      "name": "phone",
      "price": 30000
    }
  ]
}

の値が返ってきた場合、下記のサンプルコードの通りオブジェクト間を変換して最終的な値を取り出せる。

このサンプルでは、まず、{ "results" : ... } 全体は JObject で受け取り (10 – 11 行目)、results の中は配列なので JArray で受け取り (17 行目)、その配列の要素 1 つ 1 つは object なので JObject で受け取り (18 行目)、その中の値を JValue で受け取って (20, 22 行目)、最後に JValue の Value プロパティで .NET の object 型 (string, long, など) として中身を取得している (21, 23 行目)。

なお、いったん継承元である JToken で受けて、中身の構成に応じて JObject や JArray にキャストしても良い。(以降も同様)

using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Formatting;
using Newtonsoft.Json.Linq;

HttpClient cl = new HttpClient();
cl.DefaultRequestHeaders.Accept.Add(
  new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage res = await cl.GetAsync("http://example.com/someapi");
JObject jres =
  await res.Content.ReadAsAsync<JObject>(new[] { new JsonMediaTypeFormatter() });

// The following result is :
// name is pc, price is 100000.
// name is mouse, price is 5000.
// name is phone, price is 30000.
JArray jarr = (JArray)jres["results"];
foreach (JObject jobj in jarr)
{
  JValue nameValue = (JValue)jobj["name"];
  string name = (string)nameValue.Value;
  JValue priceValue = (JValue)jobj["price"];
  long price = (long)priceValue.Value;
  Console.WriteLine($"name is {name}, price is {price}.");
}

なお、上記の 10 行目で受け取った jres で、jres.ToString() とすれば、Json 全体が (改行や空白など) きれいにフォーマットされた文字列として取得できる。

例えば、下記のサンプルコードのように、いったん JToken (JObject, JArray, JValue など) を使ってオブジェクトを組み立て、それを 14 行目のように HTTP Body の文字列に変換して HTTP (REST) を投げることができる。

using System.Net.Http;
using Newtonsoft.Json.Linq;

// Sent body is :
// {
//   "eventname": "Developer Meeting",
//   "eventdate": "2016-10-24T17:30:00"
// }
JObject newObj = new JObject();
newObj.Add("eventname", new JValue("Developer Meeting"));
newObj.Add("eventdate", new JValue(new DateTime(2016, 10, 24, 17, 30, 0)));
HttpClient cl = new HttpClient();
HttpContent jsonContent = new StringContent(
  newObj.ToString(),
  Encoding.UTF8,
  "application/json");
HttpResponseMessage res = cl.PostAsync(uri, jsonContent).Result;

逆に、文字列から Json.NET の Object (JObject, JArray, 等々) を生成するには、Parse メソッドを使う。
例えば、上述の HTTP (REST API) から Json.NET のオブジェクトを取得するサンプル コードは、下記の通りいったん string で受けて (9 -10 行目)、それを Parse メソッドでオブジェクトに変換する方法で記述しても良い。(以降の処理は、上述と同じソースコードになるので省略。)

using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json.Linq;

HttpClient cl = new HttpClient();
cl.DefaultRequestHeaders.Accept.Add(
  new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage res = await cl.GetAsync("http://example.com/someapi");
string jstr =
  await res.Content.ReadAsStringAsync();
JObject jres = JObject.Parse(jstr);
...

なお、今回は、返ってくる結果が { "results" : ... } なので JObject で受けているが (上記の 11 行目)、下記の例のように、受け取る内容によって型を変える必要があるので注意してほしい。

using Newtonsoft.Json.Linq;

var obj = JObject.Parse(@"{ ""firstname"" : ""Subaru"", ""surname"" : ""Kokubun"" }");
// Result : "My family name is Kokubun."
Console.WriteLine($"My family name is {obj["surname"]}.");

var arr = JArray.Parse(@"[ { ""surname"" : ""Kokubun"" }, { ""surname"" : ""Matsuzaki"" } ]");
// Result : "Kokubun", "Matsuzaki"
foreach(var item in arr)
{
  Console.WriteLine(item["surname"]);
}

LINQ to JSON

.NET プログラマーなら IEnumerable のオブジェクトで LINQ to Object のクエリー (Select, Where など) が使えることは知っていると思うが、上記で紹介した JToken (JObject, JArray, など) も、for 文をわざわざ使わず、LINQ to JSON を使ってクエリーが実行できる。

本投稿ではこの説明は割愛するが、詳細は「Json.NET Documentation : LINQ to Json」を参照してほしい。

JsonConvert による Serialize / Desirialize

なお、Json のオブジェクトと文字列の変換には、もう 1 つ別の方法もある。

Json.NET 以前の DataContractJsonSerializer の頃から使われていた手法だが、対応するクラス構成を作成しておき、一気に Serialize / Deserialize する方法だ。
下記のサンプルコードは、上述の { "results" : ... } を変換するサンプルをこの方法で書き直したコードだ。

変換後のオブジェクトは自然なクラスなので扱いやすい一方、この方法で注意してほしいのは、あくまで静的に構成が決まっている場合しか使えないという点。中身を確認しながら動的に処理をするケースでは使えないので注意してほしい。

using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;

[JsonObject]
public class ReturnedData
{
  [JsonProperty("results")]
  public ItemData[] Results { get; set; }
}

[JsonObject]
public class ItemData
{
  [JsonProperty("id")]
  public int Id { get; set; }
  [JsonProperty("name")]
  public string Name { get; set; }
  [JsonProperty("price")]
  public int Price { get; set; }
}
...

// メインの処理
HttpClient cl = new HttpClient();
cl.DefaultRequestHeaders.Accept.Add(
  new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage res = await cl.GetAsync("http://example.com/someapi");
string jstr =
  await res.Content.ReadAsStringAsync();
ReturnedData jres = JsonConvert.DeserializeObject<ReturnedData>(jstr);
...

Json.NET は、いまや空気のように普通に使われるので、この程度のことは知っておいて損はない。

 

広告