Сопоставление JSON и XML
Модули чтения и записи, создаваемые фабрикой JsonReaderWriterFactory, обеспечивают интерфейс API XML к содержимому в формате JavaScript Object Notation (JSON, объектной нотации JavaScript). Формат JSON предусматривает кодирование данных с использованием подмножества объектных литералов JavaScript. Создаваемые этой фабрикой модули чтения и записи используются также при отправке или получении приложениями Windows Communication Foundation (WCF) JSON-содержимого с помощью элемента привязки WebMessageEncodingBindingElement или привязки WebHttpBinding.
Модуль чтения JSON при инициализации JSON-содержимым ведет себя так же, как модуль чтения текстовых XML-данных при инициализации экземпляром XML. Модуль записи JSON при получении последовательности вызовов, в результате которой модуль чтения текстовых XML-данных создает определенный экземпляр XML, записывает JSON-содержимое. В этом разделе описано сопоставление между этим экземпляром XML-данных и JSON-содержимым для использования в сложных сценариях.
При обработке внутри WCF JSON-содержимое представляется как набор сведений XML. Обычно внутреннее представление не должно заботить разработчика, поскольку сопоставление является исключительно логическим: JSON обычно не преобразуется физически в XML в памяти, равно как и XML не преобразуется в JSON. Сопоставление означает, что для обращения к JSON-содержимому используются интерфейсы API XML.
При использовании JSON в WCF обычно имеет место следующий сценарий: расширение функциональности WebScriptEnablingBehavior или расширение функциональности WebHttpBehavior автоматически подключает класс DataContractJsonSerializer, когда это необходимо. Сериализатор DataContractJsonSerializer понимает сопоставление между JSON и набором сведений XML и действует так, как будто работает непосредственно с JSON. (Можно использовать сериализатор DataContractJsonSerializer без какого-либо модуля чтения или записи XML, зная, что XML соответствует приведенному ниже сопоставлению).
В сложных сценариях может понадобиться непосредственно обратиться к приведенному ниже сопоставлению. Такие сценарии имеют место, когда требуется сериализовать десериализовать JSON особыми способами, не полагаясь на DataContractJsonSerializer, или при использовании типа Message непосредственно для сообщений, содержащих JSON. Сопоставление JSON-XML также используется для ведения журнала сообщений. При использовании функции ведения журнала сообщений в WCF сообщения JSON регистрируются в виде XML в соответствии с сопоставлением, рассмотренным в следующем разделе.
Для пояснения принципов сопоставления ниже приведен пример JSON-документа.
{"product":"pencil","price":12}
Для чтения этого JSON-документа с помощью одного из упомянутых выше модулей чтения используется та же последовательность вызовов класса XmlDictionaryReader, что и для чтения следующего XML-документа.
<root type="object">
<product type="string">pencil</product>
<price type="number">12</price>
</root>
Кроме того, если JSON-сообщение из этого примера будет получено WCF и зарегистрировано, в приведенном выше журнале будет присутствовать фрагмент XML.
Сопоставление между JSON и набором сведений XML
С формальной точки зрения, сопоставляются формат JSON, описанный в RFC 4627 (за исключением смягчения некоторых ограничений и добавления других) и набор сведений XML (не текстовые XML-данные), описанный в спецификации XML Information Set. Описания информационных единиц и полей в [квадратных скобках] можно найти в этой спецификации.
Пустой JSON-документ соответствует пустому XML-документу, а пустой XML-документ соответствует пустому JSON-документу. При сопоставлении XML-JSON пробел в начале документа и пробел в конце документа не допускаются.
Сопоставление определяется между информационной единицей документа (Document Information Item, DII) и информационной единицей элемента (Element Information Item, EII) и JSON. Информационная единица элемента (или свойство [document element] информационной единицы документа) называется корневым элементом JSON. Обратите внимание, что фрагменты документов (XML-данные с несколькими корневыми элементами) в этом сопоставлении не поддерживаются.
Пример. Как следующий документ:
<?xml version="1.0"?>
<root type="number">42</root>
так и следующий элемент:
<root type="number">42</root>
оба могут быть сопоставлены JSON. Элемент <root> является корневым элементом JSON в обоих случаях.
Кроме того, в случае DII необходимо иметь в виду следующее.
Некоторые элементы в списке [children] присутствовать не должны. Не следует полагаться на это при чтении XML-данных, полученных из JSON.
Список [children] не содержит информационных единиц комментариев.
Список [children] не содержит информационных единиц DTD.
Список [children] не содержит информационных единиц персональных данных (объявление <?xml…> не считается информационной единицей персональных данных).
Набор [notations] пуст.
Набор [unparsed entities] пуст.
Пример. Следующий документ не может быть сопоставлен JSON, поскольку список [children] содержит персональные данные и комментарий.
<?xml version="1.0"?>
<!--comment--><?pi?>
<root type="number">42</root>
EII для корневого элемента JSON имеет следующие характеристики.
Свойство [local name] имеет значение "root".
Свойство [namespace name] не имеет значения.
Свойство [prefix] не имеет значения.
Список [children] может содержать информационные единицы элементов (которые представляют внутренние элементы; см. описание ниже) или информационные единицы символов (Character Information Item, CII; см. описание ниже) либо ни то, ни другое, но не то и другое одновременно.
Набор [attributes] может содержать приведенные ниже необязательные информационные единицы атрибутов (Attribute Information Item, AII).
Атрибут типа JSON ("type") (см. описание ниже). Этот атрибут используется для сохранения типа JSON (string, number, boolean, object, array или null) в полученных в результате сопоставления XML-данных.
Атрибут имени контракта данных ("__type") (см. описание ниже). Этот атрибут может присутствовать только при условии, что присутствует также атрибут типа JSON и его свойство [normalized value] имеет значение "object". Этот атрибут используется сериализатором DataContractJsonSerializer для сохранения сведений о типе контракта данных — например, в случаях полиморфизма, где сериализуется производный тип и где ожидается базовый тип. Если используется не DataContractJsonSerializer, в большинстве случаев этот атрибут игнорируется.
Набор [in-scope namespaces] содержит привязку "xml" к "http://www.w3.org/XML/1998/namespace", как того требует спецификация наборов сведений.
Свойства [children], [attributes] и [in-scope namespaces] не должны содержать никаких единиц, кроме указанных выше, а свойство [namespace attributes] не должно иметь никаких членов; тем не менее, при чтении XML-данных, полученных из JSON, на это полагаться нельзя.
Пример. Следующий документ не может быть сопоставлен JSON, поскольку набор [namespace attributes] не пуст.
<?xml version="1.0"?>
<root xmlns:a="foo">42</root>
AII для атрибута типа JSON имеет следующие характеристики.
Свойство [namespace name] не имеет значения.
Свойство [prefix] не имеет значения.
Свойство [local name] имеет значение «type».
Свойство [normalized value] имеет одно из возможных значений-типов, описанных в следующем разделе.
Флаг [specified] имеет значение true.
Свойство [attribute type] не имеет значения.
Свойство [references] не имеет значения.
AII для атрибута имени контракта данных JSON имеет следующие характеристики.
Свойство [namespace name] не имеет значения.
Свойство [prefix] не имеет значения.
Свойство [local name] имеет значение «__type» (два знака подчеркивания и слово «type»).
Свойство [normalized value] равно любой строке Юникод — сопоставление этой строки с JSON описывается в следующем разделе.
Флаг [specified] имеет значение true.
Свойство [attribute type] не имеет значения.
Свойство [references] не имеет значения.
Внутренние элементы, содержащиеся в корневом элементе JSON или других внутренних элементах, имеют следующие характеристики.
Свойство [local name] может иметь одно из описанных ниже значений.
Свойства [namespace name], [prefix], [children], [attributes], [namespace attributes] и [in-scope namespaces] подчиняются тем же правилам, что и корневой элемент JSON.
И в корневом элементе JSON, и во внутренних элементах атрибут типа JSON определяет сопоставление с JSON, возможные дочерние информационные единицы ([children]) и их интерпретацию. В нормализованном значении атрибута (свойство [normalized value]) учитывается регистр; оно должно быть в нижнем регистре и не должно содержать пробелов.
Нормализованное значение AII атрибута типа JSON (JSON Type Attribute) | Допустимые дочерние информационные единицы соответствующей EII | Сопоставление с JSON |
---|---|---|
string (или отсутствие AII типа JSON) string и отсутствие AII типа JSON — одно и то же, поэтому string используется по умолчанию. Следовательно, |
0 или более CII |
Фрагмент JSON типа string (RFC по JSON, раздел 2.5). Каждый фрагмент типа char — это символ, соответствующий свойству [character code] из CII. Если CII нет, он сопоставляется пустому фрагменту JSON типа string. Пример. Следующий элемент сопоставляется фрагменту JSON:
Фрагмент JSON: "42". При сопоставлении XML-JSON символы, которые должны быть снабжены escape-знаком, сопоставляются символам с escape-знаком, все остальные символы сопоставляются символам без escape-знака. Символ "/" является специальным — он предваряется escape-знаком, хотя и не должен (записывается как "\/"). Пример. Следующий элемент сопоставляется фрагменту JSON:
Фрагмент JSON: "the \"da\/ta\"". При сопоставлении JSON-XML символы с escape-знаком и символы без escape-знака корректно сопоставляются соответствующему свойству [character code]. Пример. Фрагмент JSON "\u0041BC" сопоставляется следующему XML-элементу:
Строка может быть окружена пробелами ("ws" в разделе 2 RFC по JSON), которые не сопоставляются с XML. Пример. Фрагмент JSON "ABC" (с пробелами перед первой двойной кавычкой) сопоставляется следующему XML-элементу:
Любой пробел в XML сопоставляется с пробелом в JSON. Пример. Следующий XML-элемент сопоставляется фрагменту JSON:
Фрагмент JSON: " A BC ". |
number |
1 или более CII |
Фрагмент JSON типа number (RFC по JSON, раздел 2.4), возможно, окруженный пробелами. Каждый символ в комбинации число/пробел — это символ, соответствующий свойству [character code] из CII. Пример. Следующий элемент сопоставляется фрагменту JSON:
Фрагмент JSON: 42 (Пробелы сохраняются.) |
boolean |
4 или 5 CII (что соответствует true или false), возможно, окруженные дополнительными CII-пробелами. |
Последовательность CII, соответствующая строке "true", сопоставляется литералу true, а последовательность CII, соответствующая строке "false", сопоставляется литералу false. Окружающие пробелы сохраняются. Пример. Следующий элемент сопоставляется фрагменту JSON:
Фрагмент JSON: false. |
null |
Не допускается ни одного. |
Литерал null. При сопоставлении JSON-XML фрагмент null может быть окружен пробелами ("ws" в разделе 2), которые не сопоставляются с XML. Пример. Следующий элемент сопоставляется фрагменту JSON:
или
: Фрагмент JSON в обоих случаях — Null. |
object |
0 или более EII. |
Фрагмент begin-object (левая фигурная скобка), согласно разделу 2.2 RFC по JSON, после которой идет запись-член для каждой EII, как описано ниже. Если EII больше одной, между записями-членами ставятся разделители значений (запятые). После этого идет фрагмент end-object (правая фигурная скобка). Пример. Следующий элемент сопоставляется фрагменту JSON: <root type="object"> <type1 тип="string">aaa</type1> <type2 тип="string">bbb</type2> </root > Фрагмент JSON: {"type1":"aaa","type2":"bbb"}. Если в сопоставлении XML-JSON присутствует атрибут типа контракта данных, в начале вставляется дополнительная запись-член. Имя этой записи — локальное имя (свойство [local name]) атрибута типа контракта данных ("__type"), а ее значение — нормализованное значение (свойство [normalized value]) атрибута. И наоборот, при сопоставлении JSON-XML, если имя первой записи-члена представляет собой локальное имя атрибута типа контракта данных (т. е. "__type"), в полученном XML присутствует соответствующий атрибут типа контракта данных, однако соответствующая EII отсутствует. Обратите внимание, что для применения этого особого сопоставления эта запись-член должна идти первой в объекте JSON. Это отход от обычной обработки JSON, где порядок записей-членов не имеет значения. Пример. Следующий фрагмент JSON сопоставляется XML.
XML представляет собой следующий код.
Обратите внимание, что AII __type присутствует, однако EII __type — нет. Однако если порядок в JSON будет обратным, как показано в следующем примере: {"name":"John","__type":"Person"} соответствующий XML-код будет выглядеть так:
Таким образом, __type теряет особое значение и сопоставляется EII, как обычно, а не AII. Правила добавления/удаления escape-знаков для свойства [normalized value] AII при сопоставлении значению JSON такие же, как и для строк JSON (см. строку "string" выше в таблице). Пример.
Предыдущий пример может быть сопоставлен следующему фрагменту JSON.
При сопоставлении XML-JSON свойство [local name] первой EII не должно иметь значения "__type". Пробелы (ws) никогда не создаются для объектов при сопоставлении XML-JSON и не учитываются при сопоставлении JSON-XML. Пример. Следующий фрагмент JSON сопоставляется XML-элементу: { "ccc" : "aaa", "ddd" :"bbb"} XML-элемент показан в следующем коде.
|
array |
0 или более EII |
Фрагмент begin-array (левая квадратная скобка), согласно разделу 2.3 RFC по JSON, после которой идет запись-массив для каждой EII, как описано ниже. Если EII больше одной, между записями-массивами ставятся разделители значений (запятые). После этого идет фрагмент end-array. Пример. Следующий XML-элемент сопоставляется фрагменту JSON:
Фрагмент JSON: ["aaa","bbb"] Пробелы (ws) никогда не создаются для массивов при сопоставлении XML-JSON и игнорируются при сопоставлении JSON-XML. Пример. Фрагмент JSON: [ "aaa", "bbb"] XML-элемент, которому он сопоставляется:
|
Записи-члены работают следующим образом.
- Свойство [local name] внутреннего элемента сопоставляется string-части фрагмента member, согласно 2.2 RFC по JSON.
Пример. Следующий элемент сопоставляется фрагменту JSON:
<root type="object"/>
<myLocalName type="string">aaa</myLocalName>
</root >
Получается следующий фрагмент JSON:
{"myLocalName":"aaa"}
При сопоставлении XML-JSON символы, которые в JSON должны быть снабжены escape-знаком, предваряются escape-знаком, остальные символы — нет. Символ "/", хотя и не является требующим escape-знака символом, тем не менее предваряется escape-знаком (при сопоставлении JSON-XML предварять escape-знаком его не нужно). Это необходимо для поддержки формата AJAX ASP.NET для данных типа DateTime в JSON.
При сопоставлении JSON-XML все символы (включая символы без escape-знака, если необходимо) используются для формирования фрагмента типа string, который представляет собой значение свойства [local name].
Внутренние элементы (список [children]) сопоставляются значению в разделе 2.2, в соответствии с атрибутом типа JSON (JSON Type Attribute), аналогично корневому элементу JSON (Root JSON Element). Несколько уровней вложения 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 поддерживает только JSON-документы, где все символы в именах ключей представляют собой допустимые символы для имен XML-элементов. Например, JSON-документ {"<":"a"} не поддерживается, поскольку символ < не является допустимым именем для XML-элемента. |
Обратная ситуация (символы, допустимые в XML, но недопустимые в JSON) никаких проблем не вызывает, поскольку описанное выше сопоставление предусматривает добавление или удаление escape-символов JSON.
Записи-массивы работают следующим образом.
Свойство [local name] внутреннего элемента имеет значение «item».
Список [children] внутреннего элемента сопоставляется значению в разделе 2.3, в соответствии с атрибутом типа JSON, как и для корневого элемента JSON. Несколько уровней вложения 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