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>
また、WCF によってこの例の JSON メッセージが取得されてログに記録される場合は、先行ログの XML フラグメントを参照します。
JSON と XML 情報セット間のマッピング
マッピングは形式上、RFC 4627 に記述される JSON (緩和された特定の制限および追加された特定の制限を除く) と、XML Information Set (Second Edition) に記述される 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 を使用していなければ、この属性はほとんどの場合に無視されます。
XML 情報セットの仕様に規定されるように、[in-scope namespaces] には、"xml" から "http://www.w3.org/XML/1998/namespace" へのバインディングが含まれます。
[children]、[attributes]、および[in-scope namespaces] は、あらかじめ指定した項目以外の項目を持つことができず、[namespace attributes] はメンバーを持つことができませんが、JSON からマップされた XML を読み取るときには、この事実に依存しないでください。
例 : 次のドキュメントでは [namespace attributes] が空ではないため、JSON へのマッピングが存在しません。
<?xml version="1.0"?>
<root xmlns:a="foo">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 Type Attribute としての AII の [normalized value] | 対応する EII の許可された [children] | JSON へのマッピング |
---|---|---|
string (または JSON 型 AII なし) string および JSON 型 AII なしは同じです。string を既定値にします。 その結果、 |
0 個以上の CII |
JSON string (JSON RFC section 2.5)。char はそれぞれ、CII の [character code] に対応する文字です。CII が存在しない場合は、空の JSON string にマップされます。 例 : 次の要素は JSON フラグメントにマップされます。
JSON フラグメントは "42" です。 XML から JSON へのマッピングでは、エスケープする必要がある文字はエスケープ文字にマップされ、その他はすべて、エスケープされない文字にマップされます。"/" 文字は特殊です。エスケープする必要はありませんが、エスケープされます ("\/" として書き出されます)。 例 : 次の要素は JSON フラグメントにマップされます。
JSON フラグメントは "the \"da\/ta\"" です。 JSON から XML へのマッピングでは、エスケープ文字およびエスケープされない文字が、対応する [character code] に正しくマップされます。 例 : JSON フラグメント "\u0041BC" が次の XML 要素にマップされます。
文字列は、XML にマップされない空白 (JSON RFC section 2 の 'ws') で囲むことができます。 例 : JSON フラグメント "ABC" (最初の二重引用符の前に空白があります) が次の XML 要素にマップされます。
XML の空白が、JSON の空白にマップされます。 例 : 次の XML 要素は JSON フラグメントにマップされます。
JSON フラグメントは " A BC " です。 |
number |
1 個以上の CII |
JSON number (JSON RFC section 2.4)。空白で囲まれている場合があります。数値と空白の組み合わせに含まれる文字はそれぞれ、CII の [character code] に対応する文字です。 例 : 次の要素は JSON フラグメントにマップされます。
JSON フラグメントは 42 です (空白が保持されます)。 |
boolean |
4 個または 5 個の CII (true または false に対応)。追加の空白の CII で囲まれている場合があります。 |
文字列 "true" に対応する CII シーケンスは、リテラルの true にマップされ、文字列 "false" に対応する CII シーケンスは、リテラルの false にマップされます。囲んでいる空白は保持されます。 例 : 次の要素は JSON フラグメントにマップされます。
JSON フラグメントは false です。 |
null |
いずれも許可されません。 |
リテラルの null。JSON から XML へのマッピングでは、null は、XML にマップされない空白 (section 2 の 'ws') で囲まれる場合があります。 例 : 次の要素は JSON フラグメントにマップされます。
または
: いずれの場合も、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 にマップされます。
XML は次のコードです。
__type AII は存在しますが、__type EII は存在しないことに注意してください。 ただし、次の例に示すように、JSON での順序が逆になる場合があります。 {"name":"John","__type":"Person"} このとき、対応する XML は次のとおりです。
つまり、__type は特別な意味を持たなくなり、通常どおり、(AII ではなく) EII にマップされます。 JSON 値にマップされるときの、AII の [normalized value] に対するエスケープ/エスケープ解除ルールは、このテーブルの "string" 行で指定された JSON 文字列に対するルールと同じです。 例 :
これを前の例に適用すると、次の JSON にマップされます。
XML から JSON へのマッピングでは、最初の EII の [local name] が "__type" であってはなりません。 オブジェクト用の XML から JSON へのマッピングでは空白 (ws) が生成されることはなく、JSON から XML のマッピングでは空白が無視されます。 例 : 次の JSON フラグメントは XML 要素にマップされます。 { "ccc" : "aaa", "ddd" :"bbb"} XML 要素を、次のコードで示します。
|
array |
0 個以上の EII |
JSON RFC section 2.3 にあるように、begin-array (左角かっこ) の直後に、後述する各 EII の配列レコードが続きます。EII が複数個存在する場合、配列レコードの間に値区切り記号 (コンマ) が配置されます。最後尾には、end-array が置かれます。 例 : 次の XML 要素は JSON フラグメントにマップされます。
JSON フラグメントは ["aaa","bbb"] です。 配列用の XML から JSON へのマッピングでは空白 (ws) が生成されることはなく、JSON から XML のマッピングでは空白が無視されます。 例 : JSON フラグメント [ "aaa", "bbb"] マップされる XML 要素は、次のとおりです。
|
メンバー レコードの動作は次のとおりです。
- JSON RFC section 2.2 で規定されるように、内部要素の [local name] は member の string 部分にマップされます。
例 : 次の要素は 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] は Root JSON Element の場合と同じように、JSON Type Attribute に従って 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}}
注 : |
---|
上記のマッピングには、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]]
参照
リファレンス
JsonReaderWriterFactory
DataContractJsonSerializer