プロフィール

髭山髭人(ひげひと)

自分の書いた記事が、一人でも誰かの役に立てば...
活動信条の一つとして「貴方のメモは、誰かのヒント」というのがあります。

このサイトについて

本家HP packetroom.net から切り離した いわゆる技術メモ用のブログで、無料レンタルサーバーにて運用しています。広告表示はその義務なのでご容赦。
XREA さんには長年お世話になっています

Newtonsoft Json.NET でJSONを作成

概要・あらすじ

C#のデスクトップアプリにて、サーバーへJSONをPOSTする処理を組みたかったので Newtonsoft.Json のライブラリを使っていた。
で、「コレでJSONってどうやって作んねん?」となったので、 使い方というか理解を深める為?に書いたメモの延長線上的記事となります。

ちなみに 同ライブラリにおけるJSONのパース方法に関しては、別記事を過去にこさえています
Newtonsoft Json.NET を雰囲気で使う ← 読みづらい記事ですみません... 書き直すの面倒

公式ドキュメント

「このへんかな~」ってページへのリンクをちょろっと貼っておきますね
LINQ to JSON - Create JSON manually
LINQ to JSON - Create JSON using Collection Initializers

基本性質

※ 自己解釈

JObject 型 { } オブジェクトの"ガワ"部分
JArray 型 [ ] 配列の"ガワ"部分
JProperty 型 "key":"value" key:valueのペア・概念
JToken 型 value(?) value部分。ただしあやふやな状態。
これ以上細分化できるかもしれないし出来ないかもしれない
JValue 型 value value部分。これ以上細分化出来ない、末端・単体の値相当。
数値/文字列/bool とか多分その辺

キャストしたり型の確認を癖に

プロパティには当然 .Value 概念がある。
key : value の対関係で、ひとつの「プロパティ」なのだから当然。
ただ、.Value の「値」は Jvalue 型 ではなく、まず JToken 型 で扱われる。
なぜなら、.Value の皮を被っていても 実態は JObject {} だったり JArray [] だったりする可能性 があるから。
勿論、これ以上細分化できない純粋な値 ( 1 とか "hoge" とか。つまり JValue 型) の可能性も勿論ある。

JSON作成例

例 1

下記オブジェクトを作成

{
 {
   "k1":"value1" ,
   "k2": 234 ,
 }
}
//( ↓ 感覚的にはこっちかも)
{
  "k1":"value1" ,
  "k2": 234 ,
}

↓ 処理内容

JObject obj = new JObject();    // { { } }
JProperty propA = new JProperty("k1", "value1");    // { "k1" , "value1" }
JProperty propB = new JProperty("k2", 234 );    // { "k2" , 234 }
obj.Add(propA);
obj.Add(propB);

例 2

下記オブジェクトを作成

{
 {
  "k1ArrVal" : [ ],
  "k2ArrVals" : [ 1 , 2 , "hoge" ],
 }
}

↓ 処理内容

// 作成パターン 1
{
    JObject obj = new JObject();    // { { } }
    JProperty propA = new JProperty("k1ArrVal");     // { "k1ArrVal" , [](的な状態) }
    JProperty propB = new JProperty("k2ArrVals");    // { "k2ArrVals" , [](的な状態) }

    // propBのvalue である [] 部分を JArray にキャストした([]として扱う事を確定させた)うえで、
    // .Add() で値(JValue) を配列要素として放り込む
    ((JArray)(propB.Value)).Add(new JValue(1));      // { "k2ArrVals" , [ 1 ] }
    ((JArray)(propB.Value)).Add(new JValue(2));      // { "k2ArrVals" , [ 1 , 2 ] }
    ((JArray)(propB.Value)).Add(new JValue("hoge")); // { "k2ArrVals" , [ 1 , 2 , "hoge" ] }

    obj.Add(propA);
    obj.Add(propB);
}
// 作成パターン 2
{
    JObject obj = new JObject();    // { { } }
    JProperty propA = new JProperty("k1ArrVal");    // { "k1ArrVal" , [](的な状態) }
    JProperty propB = new JProperty("k2ArrVals");    // { "k2ArrVals" , [](的な状態) }
    // 一度 JArray で取り出して、そっちを追加処理に使うのもアリかも?
    // 変にキャストするよりは可読性あるかもしれない
    JArray _tempArr = (JArray)(propB.Value);
    _tempArr.Add(1);
    _tempArr.Add(2);
    _tempArr.Add("hoge");
    obj.Add(propA);
    obj.Add(propB);
}

例 3

下記オブジェクトを作成

{
 {
  "Prop_1" : { 
    "keyA" : 1 ,
    "key2" : 10
  } ,
  "Prop_2" : {
    "hoge" : "foo" ,
    "huga" : "bar"
  }
 }
}

↓ 処理内容

JObject obj = new JObject();    // { { } }
JProperty prop_1 = new JProperty("Prop_1", new JObject());     // { "Prop_1" , { } }
JProperty prop_2 = new JProperty("Prop_2", new JObject());     // { "Prop_2" , { } }

JProperty child1P1 = new JProperty("keyA", 1);     // { "keyA" , 1 }
JProperty child1P2 = new JProperty("key2", 10);     // { "key2" , 10 }

JProperty child2P1 = new JProperty("hoge", "foo");     // { "hoge" , "foo" }
JProperty child2P2 = new JProperty("huga", "bar");     // { "huga" , "bar" }

// "Prop_*" の対となる Value を JObject(つまり{})に見立てて(キャストして) .Add()で追加を図る
((JObject)(prop_1.Value)).Add(child1P1);
((JObject)(prop_1.Value)).Add(child1P2);

((JObject)(prop_2.Value)).Add(child2P1);
((JObject)(prop_2.Value)).Add(child2P2);

obj.Add(prop_1);
obj.Add(prop_2);

JValue 型だと .Add() が用意されていないので、
JObject 型として扱う事で .Add() を使えるようにする ( そしてオブジェクトの中に放り込む )
..といった感じ。

キャストを含めて、型をうまく扱うのが要なんやな~って気がします。

JProperty JValue 作成

基本形

// 同じものが出来上がる { "content" , "aa" }
JProperty _propB1 = new JProperty("content" , "aa");
JProperty _propB2 = new JProperty("content" , new JValue("aa") );

// 同じものが出来上がる { "content" , 10 }
JProperty _propB3 = new JProperty("content", 10 );
JProperty _propB4 = new JProperty("content", new JValue(10) );

作成例

空のJObjectに、プロパティを2つ突っ込む

// { { } } の "ガワ"を作成
JObject _jobjA = new JObject();

// プロパティ(key:value) を2つ作成
JProperty _propA = new JProperty("propA" , "aa");   // { "propA" , "aa" }
JProperty _propB = new JProperty("propB" , "bb");   // { "propB" , "aa" }

// 作成した2プロパティを "ガワ" に放り込む
_jobjA.Add(_propA);
_jobjA.Add(_propB);

/** できあがり
 * {
 *   "propA" , "aa" ,
 *   "propB" , "aa" ,
 * }
 */

他の例外など

以下はプロパティのキー名が重複するので例外発生 💦

JObject _jobjA = new JObject();     // { { } }
JProperty _propA = new JProperty("propA" , "aa");   // { "propA" , "aa" }
JProperty _propB = new JProperty("propA" , 1234 );  // { "propA" , 1234 }

_jobjA.Add(_propA);
_jobjA.Add(_propB);

// System.ArgumentException: 'Can not add property propA to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.'
/**
 * ※ キー名が同じ。実現不可
 * {
 *   "propA" , "aa" ,
 *   "propA" , 1234 ,
 * }
 */

例外発生ケースその2
プロパティのvalue相当に対して 値を挿入・割り当てる場合、JObject 型だと怒られる。

{
    // 同じものが出来上がる
    // { "content" , "aa" }
    JProperty _propB1 = new JProperty("content" , "aa");    
    JProperty _propB2 = new JProperty("content" , new JValue("aa") );
}

{
    // 同じものが出来上がる
    // { "content" , 10 }
    JProperty _propB3 = new JProperty("content", 10 );  
    JProperty _propB4 = new JProperty("content", new JValue(10));

    // JValue を入れなきゃいけない場所に JObject は突っ込めないと怒られる
    // { "content" , 10 ← × 追加失敗 }
    JProperty _propB5 = new JProperty("content", new JObject(10));

    // (例外発生) System.ArgumentException:
    //  'Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.'
}

ちなみに、JProperty 作成時、第2引数を省略すると value 側が配列(JArray)になる模様? Json.NET Documentation - JProperty Constructor

JProperty _propA = new JProperty("content");    // { "content" , [] }

上記ケースは JValue 側が配列なので .Add() でValue側の配列に要素を突っ込めると思いきや、出来ない

JProperty _propA = new JProperty("content");    // { "content" , [] }
JProperty _elmA = new JProperty("key1", 1 );    // { "key1" , 1 }
_propA.Add(_elmA);
// { "content" , [ { "key1" , 1 } ← × 追加失敗] }
// Newtonsoft.Json.JsonException: 'Newtonsoft.Json.Linq.JProperty cannot have multiple values.'

JProperty は(1つの)プロパティなので、.Add() で多数のプロパティを持てない
1台のゲーム機の中に、ソフトカセットは1つ挿せるが、ゲーム機1台を挿せるわけがないのと一緒

おまけ:JavaScriptなら...

型がユルいってのもあってか、このくらいカジュアルに放り込めるのですが。

{
    const Arr = [];

    Arr.push(1);
    Arr.push("あいうえお");
    Arr.push( { "key1" : "value1" } );

    console.log(Arr);
}

.js 畑の人間だと自分では思っているので、C# でこのライブラリ弄っているとき、
「元の濁りの田沼恋しき」 フレーズをつい思い浮かべてしまいましたね...