Partilhar via


Mapeando entre JSON e XML

Os leitores e gravadores produzidos pelo JsonReaderWriterFactory fornecem uma API XML para manipulação de conteúdo JSON (JavaScript Object Notation). JSON codifica dados usando um subconjunto dos literais de objeto de JavaScript. Os leitores e gravadores produzidos por essa fábrica também são usados quando o conteúdo JSON está sendo enviado ou recebido por aplicativos do Windows Communication Foundation (WCF) usando o WebMessageEncodingBindingElement ou o WebHttpBinding.

Quando inicializado com conteúdo JSON, o leitor JSON se comporta da mesma forma que um leitor XML textual faz sobre uma instância de XML. O gravador JSON, quando recebe uma sequência de chamadas que em um leitor XML textual produz uma determinada instância XML, grava o conteúdo JSON. O mapeamento entre esta instância de XML e o conteúdo JSON é descrito neste tópico para uso em cenários avançados.

Internamente, JSON é representado como um infoset XML quando processado pelo WCF. Normalmente, você não precisa se preocupar com essa representação interna, pois o mapeamento é apenas lógico: JSON normalmente não é fisicamente convertido em XML na memória ou convertido em JSON a partir de XML. O mapeamento significa que as APIs XML são usadas para acessar o conteúdo JSON.

Quando o WCF usa JSON, o cenário usual é que o DataContractJsonSerializer é automaticamente conectado pelo comportamento WebScriptEnablingBehavior ou pelo WebHttpBehavior quando apropriado. O DataContractJsonSerializer compreende o mapeamento entre JSON e o infoset XML e age como se estivesse lidando diretamente com JSON. (É possível usar o DataContractJsonSerializer com qualquer leitor ou gravador XML, com o entendimento de que o XML está em conformidade com o mapeamento a seguir.)

Em cenários avançados, pode ser necessário acessar diretamente o mapeamento a seguir. Esses cenários ocorrem quando você deseja serializar e desserializar JSON de maneiras personalizadas, sem depender do DataContractJsonSerializer, ou ao lidar com o Message tipo diretamente para mensagens que contêm JSON. O mapeamento JSON-XML também é usado para registro de mensagens. Ao usar o recurso de log de mensagens no WCF, as mensagens JSON são registradas como XML de acordo com o mapeamento descrito na próxima seção.

Para esclarecer o conceito de mapeamento, o exemplo a seguir é de um documento JSON.

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

Para ler este documento JSON usando um dos leitores mencionados anteriormente, use a mesma sequência de XmlDictionaryReader chamadas que usaria para ler o seguinte documento XML.

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

Além disso, se a mensagem JSON no exemplo for recebida pelo WCF e registrada, você verá o fragmento XML no log anterior.

Mapeando entre JSON e o Infoset XML

Formalmente, o mapeamento é entre JSON conforme descrito no RFC 4627 (exceto com certas restrições relaxadas e certas outras restrições adicionadas) e o infoset XML (e não XML textual) conforme descrito em XML Information set. Consulte este tópico para obter as definições de itens de informação e campos nos [colchetes].

Um documento JSON vazio mapeia para um documento XML vazio, e um documento XML vazio mapeia para um documento JSON vazio. No mapeamento XML para JSON, não são permitidos espaços em branco no início e espaços em branco no final após o documento.

O mapeamento é definido entre um Item de Informação do Documento (DII) ou um Item de Informação do Elemento (EII) e JSON. O EII, ou a propriedade [document element] do DII, é chamado de Elemento JSON Raiz. Observe que fragmentos de documento (XML com vários elementos raiz) não são suportados neste mapeamento.

Exemplo: O seguinte documento:

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

E o seguinte elemento:

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

Ambos têm um mapeamento para JSON. O <root> elemento é o elemento JSON raiz em ambos os casos.

Além disso, no caso de um DII, o seguinte deve ser considerado:

  • Alguns itens da lista [de crianças] não devem estar presentes. Não confie nesse fato ao ler XML mapeado a partir de JSON.

  • A lista [crianças] não contém itens de informação de comentários.

  • A lista [crianças] não contém itens de informação DTD.

  • A lista [crianças] não contém itens de informação pessoal (PI) (a declaração <?xml…> não é considerada um item de informação pessoal PI)

  • O conjunto de [notações] está vazio.

  • O conjunto [entidades não analisadas] está vazio.

Exemplo: O documento a seguir não tem mapeamento para JSON porque [filhos] contém um IP e um comentário.

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

O EII para o elemento JSON raiz tem as seguintes características:

  • [nome local] tem o valor "root".

  • [nome do namespace] não tem valor.

  • [prefixo] não tem valor.

  • [crianças] podem conter EIIs (que representam Elementos Internos, conforme descrito mais adiante) ou CIIs (Itens de Informação de Caracteres, conforme descrito mais adiante) ou nenhum destes, mas não ambos.

  • [atributos] podem conter os seguintes itens opcionais de informação de atributos (AIIs):

  • O atributo tipo JSON ("type"), conforme descrito mais adiante. Este atributo é usado para preservar o tipo JSON (string, number, boolean, object, array ou null) no XML mapeado.

  • O Atributo de Nome do Contrato de Dados ("__type"), conforme descrito mais adiante. Este atributo só pode estar presente se o atributo de tipo JSON também estiver presente e seu [valor normalizado] for "objeto". Este atributo é usado pelo DataContractJsonSerializer para preservar informações de tipo de contrato de dados - por exemplo, em casos polimórficos onde um tipo derivado é serializado e é esperado um tipo base. Se você não estiver trabalhando com o DataContractJsonSerializer, na maioria dos casos, esse atributo será ignorado.

  • [in-scope namespaces] contém a associação de "xml" com http://www.w3.org/XML/1998/namespace, conforme exigido pela especificação do infoset.

  • [children], [attributes] e [in-scope namespaces] não devem ter nenhum item além do especificado anteriormente e [namespace attributes] não deve ter membros, mas não confie nesses fatos ao ler XML mapeado a partir de JSON.

Exemplo: O documento a seguir não tem mapeamento para JSON porque [atributos de namespace] não está vazio.

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

O AII para o atributo de tipo JSON tem as seguintes características:

  • [nome do namespace] não tem valor.
  • [prefixo] não tem valor.
  • [nome local] é "tipo".
  • [valor normalizado] é um dos valores de tipo possíveis descritos na seção a seguir.
  • [especificado] é true.
  • [tipo de atributo] não tem valor.
  • [referências] não têm valor algum.

O AII para o Atributo de Nome do Contrato de Dados tem as seguintes características:

  • [nome do namespace] não tem valor.
  • [prefixo] não tem valor.
  • [nome local] é "__tipo" (dois sublinhados e depois "tipo").
  • [normalized value] é qualquer string Unicode válida – o mapeamento dessa string para JSON é descrito na seção a seguir.
  • [specified] é true.
  • [tipo de atributo] não tem valor.
  • [referências] não têm valor.

Os elementos internos contidos no elemento JSON raiz ou outros elementos internos têm as seguintes características:

  • [nome local] pode ter qualquer valor, conforme descrito mais adiante.
  • [namespace name], [prefix], [children], [attributes], [namespace attributes] e [in-scope namespaces] estão sujeitos às mesmas regras que o Elemento JSON Raiz.

Tanto no elemento JSON raiz quanto nos elementos internos, o atributo JSON Type define o mapeamento para JSON e os [filhos] disponíveis e a sua interpretação. O valor normalizado do atributo é sensível a maiúsculas e minúsculas, deve ser em minúsculas e não pode conter espaços em branco.

[valor normalizado] do AII do JSON Type Attribute Permitidos os [filhos] dos EII correspondentes Mapeamento para JSON
string (ou ausência do JSON tipo AII)

A string e a ausência do tipo JSON AII são os mesmos torna string o padrão.

Assim, <root> string1</root> mapeia para o JSON string "string1".
0 ou mais ICI A JSON string (JSON RFC, seção 2.5). Cada char é um carácter que corresponde ao [código de carácter] do CII. Se não houver CIIs, ele mapeia para um JSON stringvazio.

Exemplo: O seguinte elemento é mapeado para um fragmento JSON:

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

O fragmento JSON é "42".

No mapeamento XML para JSON, os caracteres que devem ser de escape correspondem a caracteres de escape, enquanto todos os outros correspondem a caracteres que não são de escape. O caractere "/" é especial – ele é evitado mesmo que não precise ser (representado como "\/").

Exemplo: O seguinte elemento corresponde a um fragmento JSON.

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

O fragmento JSON é "o \"da\/ta\"".

No mapeamento JSON para XML, todos os caracteres com escape e os caracteres que não são escapados mapeiam corretamente para o [código de caracteres] correspondente.

Exemplo: O fragmento JSON "\u0041BC" corresponde ao seguinte elemento XML.

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

A cadeia de caracteres pode ser envolvida por espaços em branco ('ws' na seção 2 do RFC JSON) que não são atribuídos ao XML.

Exemplo: O fragmento JSON "ABC", (há espaços antes da primeira aspa dupla), mapeia para o seguinte elemento XML.

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

Qualquer espaço em branco em XML é mapeado para espaço em branco em JSON.

Exemplo: O seguinte elemento XML é mapeado para um fragmento JSON.

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

O fragmento JSON é " A BC ".
number 1 ou mais CIIs A JSON number (JSON RFC, seção 2.4), possivelmente cercado por espaço em branco. Cada caractere na combinação número/espaço em branco é um caractere que corresponde ao [código de caracteres] do CII.

Exemplo: O elemento a seguir é mapeado para um fragmento JSON.

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

O fragmento JSON é 42

(O espaço em branco é preservado).
boolean 4 ou 5 CIIs (que corresponde a true ou false), possivelmente rodeados por CIIs adicionais de espaço em branco. Uma sequência CII que corresponde à string "true" é mapeada para o literal true, e uma sequência CII que corresponde à string "false" é mapeada para o literal false. O espaço em branco circundante é preservado.

Exemplo: O elemento a seguir corresponde a um fragmento JSON.

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

O fragmento JSON é false.
null Nenhum é permitido. O literal null. No mapeamento JSON para XML, o null pode ser cercado por espaço em branco (designado como 'ws' na secção 2) que não é mapeado para XML.

Exemplo: O elemento seguinte mapeia para um fragmento JSON.

<root type="null"/>

ou

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

:

O fragmento JSON em ambos os casos é Null.
object 0 ou mais EII. A begin-object (cinta curva esquerda) como na seção 2.2 do JSON RFC, seguido por um registro de membro para cada EII, conforme descrito mais adiante. Se houver mais de um EII, há separadores de valor (vírgulas) entre os registos dos membros. Tudo isso é seguido por um objeto final (cinta direita).

Exemplo: O elemento a seguir corresponde ao fragmento JSON.

<root type="object">

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

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

</root >

O fragmento JSON é {"type1":"aaa","type2":"bbb"}.

Se o Atributo de Tipo de Contrato de Dados estiver presente no mapeamento XML para JSON, um Registro de Membro adicional será inserido no início. Seu nome é o [nome local] do Atributo de Tipo de Contrato de Dados ("__type"), e seu valor é o [valor normalizado] do atributo. Por outro lado, no mapeamento JSON para XML, se o nome do primeiro registro de membro for o [nome local] do Atributo de Tipo de Contrato de Dados (ou seja, "__type"), um Atributo de Tipo de Contrato de Dados correspondente estará presente no XML mapeado, mas um EII correspondente não estará presente. Observe que esse registro de membro deve ocorrer primeiro no objeto JSON para que esse mapeamento especial seja aplicado. Isso representa um desvio do processamento JSON usual, onde a ordem dos registros de membros não é significativa.

Exemplo:

O fragmento JSON a seguir é mapeado para XML.

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

O XML é o código a seguir.

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

Observe que o __type AII está presente, mas não há __type EII.

No entanto, se a ordem no JSON for invertida, conforme mostrado no exemplo a seguir.

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

O XML correspondente é mostrado.

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

Ou seja, __type deixa de ter significado especial e mapeia para um EII como de costume, não para um AII.

As regras de escape/unescapeping para o [valor normalizado] do AII quando mapeado para um valor JSON são as mesmas que para strings JSON, especificadas na linha "string" desta tabela.

Exemplo:

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

O exemplo anterior pode ser mapeado para o seguinte JSON.

{"__type":"\\abc"}

Em um mapeamento XML para JSON, o primeiro EII [nome local] não deve ser "__type".

O espaço em branco (ws) nunca é gerado no mapeamento XML para JSON para objetos e é ignorado no mapeamento JSON para XML.

Exemplo: O fragmento JSON seguinte corresponde a um elemento XML.

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

O elemento XML é mostrado no código a seguir.

<root type="object"> <ccc type="string">aaa</ccc> <ddd type="string">bbb</bar> </root >
matriz 0 ou mais EII Uma matriz inicial (colchete esquerdo) como na seção 2.3 da RFC JSON, seguida por um registro de matriz para cada EII, conforme descrito mais adiante. Se houver mais de um EII, há separadores de valor (vírgulas) entre os registros de matriz. Tudo isso é seguido por uma matriz final.

Exemplo: O seguinte elemento XML é mapeado para um fragmento JSON.

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

O fragmento JSON é ["aaa","bbb"]

O espaço em branco (ws) nunca é gerado no mapeamento XML para JSON para matrizes e é ignorado no mapeamento JSON para XML.

Exemplo: Um fragmento JSON.

["aaa", "bbb"]

O elemento XML para o qual ele é mapeado.

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

Os Registos de Membros funcionam da seguinte forma:

  • O elemento interno [nome local] mapeia para a string parte do member conforme definido na seção 2.2 do JSON RFC.

Exemplo: O elemento seguinte mapeia para um fragmento JSON.

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

O fragmento JSON a seguir é exibido.

{"myLocalName":"aaa"}
  • No mapeamento XML para JSON, os caracteres que devem ser escapados em JSON são escapados e os outros não são escapados. O caractere "/", mesmo que não seja um caractere que deva ser escapado, ainda assim é escapado (não precisa ser escapado na conversão de JSON para XML). Isso é necessário para suportar o formato AJAX ASP.NET para DateTime dados em JSON.

  • No mapeamento JSON para XML, todos os caracteres (incluindo os caracteres não escapados, se necessário) são usados para formar um string que produz um [nome local].

  • Os elementos internos [subelementos] são mapeados para o valor na secção 2.2, de acordo com o JSON Type Attribute, tal como para o Root JSON Element. Vários níveis de aninhamento de EIIs (incluindo aninhamento em matrizes) são permitidos.

Exemplo: O elemento seguinte mapeia para um fragmento 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 >

O fragmento JSON a seguir é o que ele mapeia.

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

Nota

Não há nenhuma etapa de codificação XML no mapeamento anterior. Portanto, o WCF suporta apenas documentos JSON onde todos os caracteres em nomes de chave são caracteres válidos em nomes de elementos XML. Por exemplo, o documento JSON {"<":"a"} não é suportado porque < não é um nome válido para um elemento XML.

A situação inversa (caracteres válidos em XML, mas não em JSON) não causa problemas porque o mapeamento anterior inclui o processo de escape/desescape JSON.

Os registros de matriz funcionam da seguinte maneira:

  • O elemento interno [nome local] é "item".

  • Os [filhos] do elemento interno são mapeados para o valor na seção 2.3, de acordo com o Atributo de Tipo JSON, como acontece com o Elemento JSON Raiz. Vários níveis de aninhamento de EIIs são permitidos (incluindo o aninhamento dentro de objetos).

Exemplo: O elemento seguinte mapeia para um fragmento 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>

A seguir está o fragmento JSON.

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

Consulte também