JSON と XML 間のマッピング

JsonReaderWriterFactory によって作成されるリーダーとライターには、JSON (JavaScript Object Notation) コンテンツでの XML API が備わっています。 JSON は、JavaScript のオブジェクト リテラルのサブセットを使用してデータをエンコードします。 このファクトリによって作成されるリーダーおよびライターは、Windows Communication Foundation (WCF) アプリケーションが WebMessageEncodingBindingElement または WebHttpBinding を使用して JSON コンテンツを送信または受信するときにも使用されます。

JSON リーダーは JSON コンテンツで初期化されると、XML インスタンスで動作するテキストの XML リーダーと同じ方法で動作します。 テキストの XML リーダーでの一連の呼び出しによって特定の XML インスタンスが生成されると、JSON ライターは JSON コンテンツを書き出します。 このトピックでは、高度なシナリオで使用するために、この XML のインスタンスと JSON コンテンツ間のマッピングについて説明します。

内部的に、JSON は、WCF によって処理される際に XML 情報セットとして表されます。 マッピングは論理上の処理であるため、通常はこのような内部表現を考慮する必要はありません。JSON は通常、メモリで物理的に XML に変換されたり、XML から JSON に変換されたりすることはありません。 マッピングとは、XML API を使用して JSON コンテンツにアクセスすることを意味します。

WCF で JSON を使用する場合、通常のシナリオでは、WebScriptEnablingBehavior 動作、または該当する場合は WebHttpBehavior 動作により、DataContractJsonSerializer が自動的にプラグインされます。 DataContractJsonSerializer は、JSON と XML 情報セット間のマッピングを認識し、直接 JSON を処理しているかのように動作します。 XML が次のマッピングに従うという了解の下に、任意の XML リーダーまたはライターで DataContractJsonSerializer を使用できます。

高度なシナリオでは、次のマッピングへの直接アクセスが必要になることがあります。 このようなシナリオが生じるのは、DataContractJsonSerializer に依存することなく、独自の方法で JSON のシリアル化および逆シリアル化を行うとき、または JSON を含むメッセージに対して直接 Message 型を処理するときです。 JSON と XML 間のマッピングは、メッセージ ログにも使用されます。 WCF のメッセージ ログ機能を使用する場合は、次のセクションで説明するマッピングに従って、JSON メッセージが XML としてログに記録されます。

マッピングの概念を明確にするために、次に JSON ドキュメントの例を示します。

{"product":"pencil","price":12}

前述のリーダーのいずれか 1 つを使用してこの JSON ドキュメントを読み取るには、次の XML ドキュメントの読み取りに使用するシーケンスと同一の、XmlDictionaryReader 呼び出しのシーケンスを使用します。

<root type="object">
    <product type="string">pencil</product>
    <price type="number">12</price>
</root>

また、この例の JSON メッセージが WCF によって取得され、ログに記録される場合は、先行ログの XML フラグメントを参照します。

JSON と XML 情報セット間のマッピング

マッピングは形式上、RFC 4627 に記述される JSON (緩和された特定の制限、および追加された特定の制限を除く) と、XML Information Set に記述される XML 情報セット (テキストの XML ではない) との間に存在します。 情報項目および角かっこ "[]" で囲まれたフィールドの定義については、このトピックを参照してください。

空白の JSON ドキュメントは空白の XML ドキュメントに、空白の XML ドキュメントは空白の JSON ドキュメントにマップされます。 XML から JSON へのマッピングで、先行する空白、およびドキュメントの後に続く空白は許可されません。

マッピングは、ドキュメント情報項目 (DII: Document Information Item) または要素情報項目 (EII: Element Information Item) のいずれか一方と JSON の間に定義されます。 EII、または DII の [document element] プロパティは、Root JSON Element と呼ばれます。 また、ドキュメント フラグメント (ルート要素を複数持つ XML) は、このマッピングではサポートされません。

例 : 次のドキュメントがあるとします。

<?xml version="1.0"?>
<root type="number">42</root>

また、次の要素があるとします。

<root type="number">42</root>

どちらにも JSON へのマッピングが存在します。 <root> 要素は、どちらの場合も Root JSON Element です。

また、DII の場合は次の点を考慮する必要があります。

  • [children] 一覧には、除外する必要のある項目が含まれています。 JSON からマッピングされた XML を読み取るときは、この事実に依存しないでください。

  • [children] 一覧には、注釈情報項目が保持されません。

  • [children] 一覧には、DTD 情報項目が保持されません。

  • [children] 一覧には、個人情報 (PI: Personal Information) 情報項目が保持されません (<?xml…> 宣言は PI 情報項目と見なされません)。

  • [notations] セットは空です。

  • [unparsed entities] セットは空です。

例 : 次のドキュメントでは [children] が PI とコメントを保持するため、JSON へのマッピングが存在しません。

<?xml version="1.0"?>
<!--comment--><?pi?>
<root type="number">42</root>

Root JSON Element としての EII には次の特性があります。

  • [local name] は値 "root" を持ちます。

  • [namespace name] は値を持ちません。

  • [prefix] は値を持ちません。

  • [children] には、後述するように内部要素である EII、または文字情報項目 (CII: Character Information Item) のいずれか一方が含まれる場合と、どちらも含まれない場合がありますが、両方とも含まれることはありません。

  • [attributes] には、次のオプションの属性情報項目 (AII: Attribute Information Item) が含まれる場合があります。

  • 後述の JSON Type Attribute ("type")。 この属性は、マップされた XML で JSON 型 (文字列、数値、ブール値、オブジェクト、配列、または null) を保持するのに使用します。

  • 後述の Data Contract Name Attribute ("__type")。 この属性は、JSON Type Attribute が存在し、その [normalized value] が "object" であるときにのみ存在します。 この属性は、派生型がシリアル化されたり、基本型が要求されたりするポリモーフィックな場合などに、DataContractJsonSerializer によりデータ コントラクトの型情報を保持するために使用されます。 DataContractJsonSerializer を使用していなければ、この属性はほとんどの場合に無視されます。

  • [スコープ内の名前空間] には、infoset 仕様で規定されているように、http://www.w3.org/XML/1998/namespace への "xml" のバインディングが含まれています。

  • [children]、[attributes]、および[in-scope namespaces] は、あらかじめ指定した項目以外の項目を持つことができず、[namespace attributes] はメンバーを持つことができませんが、JSON からマップされた XML を読み取るときには、この事実に依存しないでください。

例 : 次のドキュメントでは [namespace attributes] が空ではないため、JSON へのマッピングが存在しません。

<?xml version="1.0"?>
<root xmlns:a="myattributevalue">42</root>

JSON Type Attribute としての AII には次の特性があります。

  • [namespace name] は値を持ちません。
  • [prefix] は値を持ちません。
  • [local name] は "type" です。
  • [normalized value] は、次のセクションで説明する型の値のいずれかになります。
  • [specified] は true です。
  • [attribute type] は値を持ちません。
  • [references] は値を持ちません。

Data Contract Name Attribute としての AII には次の特性があります。

  • [namespace name] は値を持ちません。
  • [prefix] は値を持ちません。
  • [local name] は "__type" (2 連続のアンダースコアの直後に "type") です。
  • [normalized value] は、任意の有効な Unicode 文字列です。この文字列から JSON へのマッピングは、次のセクションで説明します。
  • [specified] は true です。
  • [attribute type] は値を持ちません。
  • [references] は値を持ちません。

Root JSON Element に含まれる内部要素、またはその他の内部要素には、次の特性があります。

  • 後述のように、[local name] は、任意の値を持つことができます。
  • [namespace name]、[prefix]、[children]、[attributes]、[namespace attributes]、および [in-scope namespaces] は、Root JSON Element と同じ規則に従います。

Root JSON Element および内部要素では、JSON Type Attribute により JSON へのマッピングと、[children] およびその解釈が定義されます。 この属性の [normalized value] では、大文字と小文字が区別されており、小文字を使用する必要があります。また、空白を含めることはできません。

JSON 型属性の AII の [normalized value] 対応する EII の許可された [children] JSON へのマッピング
string (または JSON 型 AII なし)

string および JSON 型 AII なしは同じです。string を既定値にします。

その結果、<root> string1</root> が JSON string "string1" にマップされます。
0 個以上の CII JSON string (JSON RFC section 2.5)。 char はそれぞれ、CII の [character code] に対応する文字です。 CII が存在しない場合は、空の JSON string にマップされます。

例 : 次の要素は JSON フラグメントにマップされます。

<root type="string">42</root>

JSON フラグメントは "42" です。

XML から JSON へのマッピングでは、エスケープする必要がある文字はエスケープ文字にマップされ、その他はすべて、エスケープされない文字にマップされます。 "/" 文字は特殊です。エスケープする必要はありませんが、エスケープされます ("\/" として書き出されます)。

例 : 次の要素は JSON フラグメントにマップされます。

<root type="string">the "da/ta"</root>

JSON フラグメントは "the \"da\/ta\"" です。

JSON から XML へのマッピングでは、エスケープ文字およびエスケープされない文字が、対応する [character code] に正しくマップされます。

例 : JSON フラグメント "\u0041BC" が次の XML 要素にマップされます。

<root type="string">ABC</root>

文字列は、XML にマップされない空白 (JSON RFC section 2 の 'ws') で囲むことができます。

例 : JSON フラグメント "ABC" (最初の二重引用符の前に空白があります) が次の XML 要素にマップされます。

<root type="string">ABC</root>

XML の空白文字はいずれも、JSON の空白文字にマップされます。

例 : 次の XML 要素は JSON フラグメントにマップされます。

<root type="string"> A BC </root>

JSON フラグメントは " A BC " です。
number 1 個以上の CII JSON number (JSON RFC section 2.4)。空白で囲まれている場合があります。 数値と空白の組み合わせに含まれる文字はそれぞれ、CII の [character code] に対応する文字です。

例 : 次の要素は JSON フラグメントにマップされます。

<root type="number"> 42</root>

JSON フラグメントは 42 です

(空白は保持されます)。
boolean 4 個または 5 個の CII (true または false に対応)。追加の空白の CII で囲まれている場合があります。 文字列 "true" に対応する CII シーケンスは、リテラルの true にマップされ、文字列 "false" に対応する CII シーケンスは、リテラルの false にマップされます。 囲んでいる空白は保持されます。

例 : 次の要素は JSON フラグメントにマップされます。

<root type="boolean"> false</root>

JSON フラグメントは false です。
null いずれも許可されません。 リテラルの null。 JSON から XML へのマッピングでは、null は、XML にマップされない空白 (section 2 の 'ws') で囲まれる場合があります。

例 : 次の要素は JSON フラグメントにマップされます。

<root type="null"/>

or

<root type="null"></root>

:

いずれの場合も、JSON フラグメントは Null です。
object 0 個以上の EII JSON RFC section 2.2 にあるように、begin-object (左中かっこ) の直後に、後述するように各 EII のメンバー レコードが続きます。 EII が複数個存在する場合、メンバー レコードの間に値区切り記号 (コンマ) が配置されます。 最後尾には、end-object (右中かっこ) が置かれます。

例 : 次の要素は JSON フラグメントにマップされます。

<root type="object">

<type1 type="string">aaa\</type1>

<type2 type="string">bbb\</type2>

</root >

JSON フラグメントは {"type1":"aaa","type2":"bbb"} です。

XML から JSON へのマッピングに Data Contract Type Attribute が存在する場合は、最初に追加のメンバー レコードが挿入されます。 その名前は Data Contract Type Attribute ("__type") の [local name] であり、その値はこの属性の [normalized value] です。 逆に、JSON から XML へのマッピングでは、最初のメンバー レコードの名前は Data Contract Type Attribute ("__type") の [local name] であり、マップされた XML には対応する Data Contract Type Attribute が存在しますが、対応する EII は存在しません。 また、このような特定のマッピングが適用されるには、このメンバー レコードが最初に JSON オブジェクトに存在している必要があります。 これは、メンバー レコードの順序が重要ではない通常の JSON 処理とは異なっています。

例:

次の JSON フラグメントは XML にマップされます。

{"__type":"Person","name":"John"}

XML は次のコードです。

<root type="object" __type="Person"> <name type="string">John</name> </root>

__type AII は存在しますが、__type EII は存在しないことに注意してください。

ただし、次の例に示すように、JSON での順序が逆になる場合があります。

{"name":"John","\_\_type":"Person"}

このとき、対応する XML は次のとおりです。

<root type="object"> <name type="string">John</name> <__type type="string">Person</__type> </root>

つまり、__type は特別な意味を持たなくなり、通常どおり、(AII ではなく) EII にマップされます。

JSON 値にマップされるときの、AII の [normalized value] に対するエスケープ/エスケープ解除ルールは、このテーブルの "string" 行で指定された JSON 文字列に対するルールと同じです。

例:

<root type="object" __type="\abc" />

これを前の例に適用すると、次の JSON にマップされます。

{"__type":"\\abc"}

XML から JSON へのマッピングでは、最初の EII の [local name] が "__type" であってはなりません。

オブジェクト用の XML から JSON へのマッピングでは空白 (ws) が生成されることはなく、JSON から XML のマッピングでは空白が無視されます。

例 : 次の JSON フラグメントは XML 要素にマップされます。

{ "ccc" : "aaa", "ddd" :"bbb"}

XML 要素を、次のコードで示します。

<root type="object"> <ccc type="string">aaa</ccc> <ddd type="string">bbb</bar> </root >
array 0 個以上の EII JSON RFC section 2.3 にあるように、begin-array (左角かっこ) の直後に、後述する各 EII の配列レコードが続きます。 EII が複数個存在する場合、配列レコードの間に値区切り記号 (コンマ) が配置されます。 最後尾には、end-array が置かれます。

例 : 次の XML 要素は JSON フラグメントにマップされます。

<root type="array"/> <item type="string">aaa</item> <item type="string">bbb</item> </root >

JSON フラグメントは ["aaa","bbb"] です。

配列用の XML から JSON へのマッピングでは空白 (ws) が生成されることはなく、JSON から XML のマッピングでは空白が無視されます。

例: JSON フラグメント。

["aaa", "bbb"]

マップされる XML 要素は、次のとおりです。

<root type="array"/> <item type="string">aaa</item> <item type="string">bbb</item> </root >

メンバー レコードの動作は次のとおりです。

  • JSON RFC section 2.2 で規定されるように、内部要素の [local name] は stringmember 部分にマップされます。

例 : 次の要素は JSON フラグメントにマップされます。

<root type="object">
    <myLocalName type="string">aaa</myLocalName>
</root>

次の JSON フラグメントが示されます。

{"myLocalName":"aaa"}
  • XML から JSON へのマッピングでは、JSON でエスケープする必要がある文字はエスケープされ、それ以外はエスケープされません。 "/" 文字は、エスケープする必要のない文字ですが、エスケープされます (JSON から XML へのマッピングではエスケープする必要はありません)。 これは、JSON の DateTime データに対する ASP.NET AJAX 形式をサポートするために必要な処理です。

  • JSON から XML へのマッピングでは、すべての文字が (必要に応じて非エスケープ文字も含む) [local name] を生成する string を形成するために使用されます。

  • 内部要素 [children] は JSON Type Attribute の場合と同じように、Root JSON Element に従って section 2.2 の値にマップされます。 EII の複数レベルの入れ子 (配列内の入れ子も含む) が許可されます。

例 : 次の要素は JSON フラグメントにマップされます。

<root type="object">
    <myLocalName1 type="string">myValue1</myLocalName1>
    <myLocalName2 type="number">2</myLocalName2>
    <myLocalName3 type="object">
        <myNestedName1 type="boolean">true</myNestedName1>
        <myNestedName2 type="null"/>
    </myLocalName3>
</root >

マップされる JSON フラグメントは次のとおりです。

{"myLocalName1":"myValue1","myLocalName2":2,"myLocalName3":{"myNestedName1":true,"myNestedName2":null}}

Note

上記のマッピングには、XML エンコーディングの手順がありません。 したがって、WCF は、キー名のすべての文字が XML 要素名の有効な文字である JSON ドキュメントのみをサポートします。 たとえば、JSON ドキュメント {"<":"a"} は、< が XML 要素の有効な名前ではないため、サポートされません。

逆に、XML では有効であり、JSON では有効でない文字は、上記のマッピングに JSON のエスケープ/エスケープ解除の手順が含まれるため、問題が生じません。

Array Record の動作は次のとおりです。

  • 内部要素の [local name] は "item" です。

  • 内部要素の [children] は、Root JSON Element の場合と同じように、JSON Type Attribute に従って section 2.3 の値にマップされます。 EII の複数の入れ子 (オブジェクト内の入れ子も含む) が許可されます。

例 : 次の要素は JSON フラグメントにマップされます。

<root type="array">
    <item type="string">myValue1</item>
    <item type="number">2</item>
    <item type="array">
    <item type="boolean">true</item>
    <item type="null"/></item>
</root>

JSON フラグメントは次のとおりです。

["myValue1",2,[true,null]]

関連項目