Сопоставление JSON и XML.

Модули чтения и записи, создаваемые фабрикой JsonReaderWriterFactory, предоставляют XML API к содержимому в формате JavaScript Object Notation (JSON, объектная нотация JavaScript). Формат JSON предусматривает кодирование данных с использованием подмножества объектных литералов JavaScript. Средства чтения и записи, созданные этой фабрикой, также используются при отправке или получении JSON-контента приложениями Windows Communication Foundation (WCF) с помощью WebMessageEncodingBindingElement или WebHttpBinding.

При инициализации JSON-содержимым модуль JSON ведет себя так же, как текстовый XML-ридер при работе с экземпляром XML. Модуль записи JSON при получении последовательности вызовов, в результате которой модуль чтения текстовых XML-данных создает определенный экземпляр XML, записывает JSON-содержимое. В этом разделе описано сопоставление между этим экземпляром XML-данных и JSON-содержимым для использования в сложных сценариях.

Во внутренней среде JSON представляется в виде xml-набора сведений при обработке WCF. Обычно внутреннее представление не должно заботить разработчика, поскольку сопоставление является исключительно логическим: JSON обычно не преобразуется физически в XML в памяти, равно как и XML не преобразуется в JSON. Сопоставление означает, что для обращения к JSON-содержимому используются интерфейсы API XML.

Если WCF использует JSON, обычный сценарий заключается в том, что DataContractJsonSerializer автоматически подключается через поведение WebScriptEnablingBehavior, или через поведение WebHttpBehavior, когда это необходимо. Сериализатор 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. Информационная единица элемента (EII), или свойство элемента документа DII, называется корневым элементом 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] могут содержать следующие необязательные атрибутные информационные единицы (AII).

  • Атрибут типа JSON ("type") (см. описание ниже). Этот атрибут используется для сохранения типа JSON (string, number, boolean, object, array или null) в полученных в результате сопоставления XML-данных.

  • Атрибут имени контракта данных ("__type") как описано далее. Этот атрибут может присутствовать только при условии, что присутствует также атрибут типа JSON и его свойство [normalized value] имеет значение "object". Этот атрибут используется сериализатором DataContractJsonSerializer для сохранения информации о типе контракта данных - например, в полиморфных случаях, когда сериализуется производный тип и ожидается базовый тип. Если вы не работаете с DataContractJsonSerializer, в большинстве случаев этот атрибут игнорируется.

  • [пространства имен, входящие в область] содержит привязку "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="myattributevalue">42</root>

AII для атрибута типа JSON имеет следующие характеристики.

  • Свойство [namespace name] не имеет значения.
  • Свойство [prefix] не имеет значения.
  • Свойство [local name] имеет значение «type».
  • Свойство [normalized value] является одним из возможных типов значений, описанных в следующем разделе.
  • Флаг [specified] имеет значение true.
  • Свойство [attribute type] не имеет значения.
  • Свойство [references] не имеет значения.

AII для атрибута имени контракта данных имеет следующие характеристики.

  • Свойство [namespace name] не имеет значения.
  • Свойство [prefix] не имеет значения.
  • [локальное имя] — "__type" (два символа подчеркивания и "тип").
  • Свойство [normalized value] равно любой строке Юникод - сопоставление этой строки с JSON описывается в следующем разделе.
  • [specified] — это true.
  • Свойство [attribute type] не имеет значения.
  • Свойство [references] не имеет значения.

Внутренние элементы, содержащиеся в корневом элементе JSON или других внутренних элементах, имеют следующие характеристики.

  • [локальное имя] может иметь любое значение, как описано далее.
  • Свойства [namespace name], [prefix], [children], [attributes], [namespace attributes] и [in-scope namespaces] подчиняются тем же правилам, что и корневой элемент JSON.

Как в корневом элементе JSON, так и во внутренних элементах, атрибут типа JSON определяет сопоставление с JSON, возможные дочерние элементы и их интерпретацию. Значение атрибута [нормализованное значение] является чувствительным к регистру, должно быть в нижнем регистре и не может содержать пробелы.

[нормализованное значение] AII атрибута типа JSON Допустимые дочерние информационные единицы соответствующей EII Преобразование в JSON
string (или отсутствие AII типа JSON)

string и отсутствие AII типа JSON - одно и то же, поэтому string используется по умолчанию.

Следовательно, <root> string1</root> соответствует string "string1" в JSON.
0 или более CIIs Фрагмент JSON типа string (RFC по JSON, раздел 2.5). Каждый фрагмент типа char - это символ, соответствующий свойству [character code] из CII. Если CII нет, он сопоставляется с пустым фрагментом JSON типа string.

Пример. Следующий элемент сопоставляется фрагменту JSON:

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

Фрагмент JSON: "42".

При сопоставлении XML-JSON символы, которые должны быть снабжены escape-символом, сопоставляются символам с escape-символом, все остальные символы сопоставляются символам без escape-символа. Символ "/" является специальным – он экранируется, даже если в этом нет необходимости (записан как "\/").

Пример. Следующий элемент сопоставляется фрагменту JSON:

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

Фрагмент JSON — "da\/ta\".

При сопоставлении JSON-XML символы с escape-знаком и символы без escape-знака корректно сопоставляются соответствующему свойству [character code].

Пример. Фрагмент JSON "\u0041BC" сопоставляется следующему XML-элементу:

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

Строка может быть окружена пробелами ("ws" в разделе 2 RFC JSON), которые не переносятся в XML.

Пример. Фрагмент JSON "ABC" (с пробелами перед первой двойной кавычкой) сопоставляется следующему XML-элементу:

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

Любой пробел в XML-файле сопоставляется с пробелом в JSON.

Пример. Следующий XML-элемент сопоставляется фрагменту JSON:

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

Фрагмент JSON: " A BC ".
number Один или более CII JSON number (JSON RFC, раздел 2.4), возможно, окруженный пробелом. Каждый символ в сочетании чисел и пробелов — это символ, соответствующий [коду символов] из CII.

Пример. Следующий элемент сопоставляется фрагменту JSON:

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

Фрагмент JSON: 42

(Пробел сохраняется).
boolean 4 или 5 CIIs (которые соответствуют true или false), возможно, окружены дополнительными пробелами CIIs. Последовательность CII, соответствующая строке "true", сопоставляется литералу true, а последовательность CII, соответствующая строке "false", сопоставляется литералу false. Окружающие пробелы сохраняются.

Пример. Следующий элемент сопоставляется фрагменту JSON:

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

Фрагмент JSON: false.
null Ничего не допускается. Литерал null. При сопоставлении JSON с XML, null может быть обрамлено пробелами ("ws" в разделе 2), которые не сопоставляются с XML.

Пример. Следующий элемент сопоставляется фрагменту JSON:

<root type="null"/>

или

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

:

Фрагмент JSON в обоих случаях - Null.
object 0 или более EII. Символ begin-object (левая фигурная скобка), согласно разделу 2.2 RFC по JSON, после которой идет запись члена для каждой 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 присутствует атрибут типа контракта данных, в начале добавляется дополнительная запись о члене. Его имя — это [локальное имя] атрибута типа контракта данных ("__type"), а его значение — это [нормализованное значение] атрибута. И наоборот, при сопоставлении JSON с XML, если имя первой записи совпадает с [локальным именем] атрибута типа контракта данных (то есть "__type"), в сопоставленном XML присутствует соответствующий атрибут типа контракта данных, но соответствующий 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 перестает иметь особое значение и сопоставляется с EII как это обычно происходит, а не AII.

Правила экранирования/снятия экранирования для свойства [normalized value] AII при сопоставлении с JSON-значением такие же, как и для строк JSON, указано в строке 'строка' данной таблицы.

Пример:

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

Предыдущий пример может быть сопоставлен следующему фрагменту JSON.

{"__type":"\\abc"}

В сопоставлении XML с JSON первый EII [локальное имя] не должен быть "__type".

Пробелы (ws) никогда не создаются при сопоставлении XML с JSON для объектов и игнорируются при обратном сопоставлении JSON с XML.

Пример. Следующий фрагмент JSON сопоставляется XML-элементу:

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

XML-элемент показан в следующем коде.

<root type="object"> <ccc type="string">aaa</ccc> <ddd type="string">bbb</bar> </root >
массив 0 или более EII Начало массива (левая квадратная скобка), согласно разделу 2.3 RFC по JSON, после которой идет запись массива для каждой EII, как описано далее. Если EII более одного, между записями массива ставятся разделители значений (запятые). После этого идет фрагмент end-array.

Пример. Следующий XML-элемент сопоставляется фрагменту JSON:

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

Фрагмент JSON это ["aaa","bbb"]

Пробелы (ws) никогда не создаются при сопоставлении XML с JSON для массивов и игнорируются при сопоставлении JSON с XML.

Пример: фрагмент JSON.

["aaa", "bbb"]

XML-элемент, которому он сопоставляется:

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

Членские записи функционируют следующим образом.

  • Свойство [local name] внутреннего элемента соответствует части string элемента member, как определено в разделе 2.2 RFC по JSON.

Пример. Следующий элемент сопоставляется фрагменту JSON:

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

Получается следующий фрагмент JSON:

{"myLocalName":"aaa"}
  • При преобразовании из XML в JSON символы, которые в JSON требуют экранирования, экранируются, остальные символы — нет. Символ "/" сам по себе не требует экранирования, однако он всё равно экранируется (при преобразовании JSON в XML экранирование не требуется). Это необходимо для поддержки формата AJAX ASP.NET для данных типа DateTime в JSON.

  • При сопоставлении JSON-XML все символы (включая символы без escape-знака, если необходимо) используются для формирования фрагмента типа string, который представляет собой значение свойства [local name].

  • Внутренние элементы [children] сопоставляются со значением в разделе 2.2, согласно JSON Type Attribute, так же как для Root JSON Element. Несколько уровней вложенности элементов информационной инфраструктуры (включая вложенность в массивах) допускаются.

Пример. Следующий элемент сопоставляется фрагменту 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) не создаёт никаких проблем, поскольку предыдущее сопоставление включает шаги по экранированию и деэкранированию 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]]

См. также