Mapeamento entre JSON e XML
Os leitores e gravadores produzidos por JsonReaderWriterFactory mostram um API de XML sobre o conteúdo JSON (JavaScript Object Notation). O JSON codifica dados usando um subconjunto de literais de objeto do JavaScript. Os leitores e gravadores produzidos por este alocador também são usados quando o conteúdo JSON está sendo enviado ou recebido por aplicativos do WCF (Windows Communication Foundation) usando WebMessageEncodingBindingElement ou WebHttpBinding.
Quando inicializado com conteúdo JSON, o leitor JSON se comporta da mesma forma que um leitor de XML textual em uma instância de XML. O gravador JSON, quando dada uma sequência de chamadas que um leitor de XML textual produz uma determinada instância XML, grava o conteúdo JSON. O mapeamento entre essa instância de XML e o conteúdo JSON é descrito neste tópico para uso em cenários avançados.
Internamente, o JSON é representado como um infoset de XML quando processado pelo WCF. Normalmente, você não precisa se preocupar com essa representação interna, pois o mapeamento é apenas lógico: o JSON normalmente não é convertido fisicamente em XML na memória ou convertido em JSON de XML. O mapeamento significa que as APIs de XML são usadas para acessar o conteúdo JSON.
Quando o WCF usa JSON, o cenário usual é que DataContractJsonSerializer é conectado automaticamente pelo comportamento WebScriptEnablingBehavior ou pelo comportamento WebHttpBehavior quando apropriado. O DataContractJsonSerializer entende o mapeamento entre JSON e o infoset de XML e atua como se estivesse lidando diretamente com JSON. (É possível usar DataContractJsonSerializer com qualquer leitor ou gravador XML, com a compreensão de que o XML está em conformidade com o mapeamento a seguir.)
Em cenários avançados, pode ser necessário ter acesso direto ao mapeamento a seguir. Esses cenários ocorrem quando você deseja serializar e desserializar o JSON de forma personalizada, sem depender do DataContractJsonSerializer ou ao lidar com o tipo Message diretamente para mensagens que contêm JSON. O mapeamento JSON-XML também é usado para registro em log de mensagens. Ao usar o recurso de registro em 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 um 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 chamadas XmlDictionaryReader que você faria para ler o documento XML a seguir.
<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 em log, você veria o fragmento XML no log anterior.
Mapeamento entre JSON e XML Infoset
Formalmente, o mapeamento é feito entre JSON, conforme descrito no RFC 4627 (exceto com determinadas restrições flexibilizadas e outras restrições adicionadas) e o XML Infoset (e não XML textual), conforme descrito no XML Information Set. Consulte este tópico para obter as definições de itens de informações e campos em [colchetes].
Um documento JSON em branco mapeia um documento XML em branco e um documento XML em branco mapeia um documento JSON em branco. No mapeamento XML para JSON, o espaço em branco anterior e o espaço em branco à direita após o documento não são permitidos.
O mapeamento é definido entre um DII (Document Information Item) ou um EII (Element Information Item) e JSON. O EII, ou a propriedade [elemento de documento] do DII, é chamado de Elemento JSON Raiz. Observe que não há suporte para fragmentos de documento (XML com vários elementos raiz) neste mapeamento.
Exemplo: o seguinte documento:
<?xml version="1.0"?>
<root type="number">42</root>
E o elemento a seguir:
<root type="number">42</root>
Ambos têm um mapeamento para JSON. O elemento <root>
é o Elemento JSON Raiz em ambos os casos.
Além disso, no caso de um DII, deve ser considerado:
Alguns itens na lista [filhos] não devem estar presentes. Não confie nesse fato ao ler XML mapeado do JSON.
A lista [filhos] não contém itens de informações de comentário.
A lista [filhos] não contém itens de informações DTD.
A lista [filhos] não contém itens de informações de PI (informações pessoais) (a declaração
<?xml…>
não é considerada um item de informações de PI)O conjunto [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 PI e um comentário.
<?xml version="1.0"?>
<!--comment--><?pi?>
<root type="number">42</root>
O EII para o elemento raiz JSON tem as seguintes características:
[nome local] tem o valor "raiz".
[nome do namespace] não tem valor.
[prefixo] não tem valor.
[filhos] podem conter EIIs (que representam elementos internos conforme descrito abaixo) ou CIIs (Itens de informações de caracteres, conforme descrito abaixo) ou nenhum deles, mas não nos dois.
[atributos] podem conter os seguintes itens de informações de atributo opcionais (AIIs)
O atributo de tipo JSON ("tipo") conforme descrito abaixo. Esse atributo é usado para preservar o tipo JSON (cadeia de caracteres, número, booliano, objeto, matriz ou nulo) no XML mapeado.
O atributo nome do contrato de dados ("__type") conforme descrito abaixo. Esse atributo somente pode estar presente se o atributo de tipo JSON também estiver presente e seu [valor normalizado] for "objeto". Esse atributo é usado por
DataContractJsonSerializer
para manter as informações de tipo de contrato de dados, por exemplo, em casos polimórficos em que um tipo derivado é serializado e onde um tipo base é esperado. Se você não estiver trabalhando comDataContractJsonSerializer
, na maioria dos casos, esse atributo será ignorado.[namespaces no escopo] contém a associação de "XML" para
http://www.w3.org/XML/1998/namespace
, conforme exigido pela especificação do infoset.[filhos], [atributos] e [namespaces no escopo] não devem ter nenhum item diferente do especificado anteriormente e [atributos de namespace] não devem ter membros, mas não dependem desses fatos ao ler XML mapeado do 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 tem valor.
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] é "__type" (dois sublinhados e, em seguida, "type").
- [valor normalizado] é qualquer cadeia de caracteres Unicode válida – o mapeamento dessa cadeia de caracteres para JSON é descrito na seção a seguir.
- [especificado] é
true
. - [tipo de atributo] não tem valor.
- [referências] não tem valor.
Elementos internos contidos no elemento raiz JSON ou em outros elementos internos têm as seguintes características:
- [nome local] pode ter qualquer valor, conforme descrito abaixo.
- [nome do namespace], [prefixo], [filhos], [atributos], [atributos de namespace], e [namespaces no escopo] estão sujeitos às mesmas regras que o elemento raiz JSON.
No elemento raiz JSON e nos elementos internos, o Atributo de Tipo JSON define o mapeamento para JSON e os possíveis [filhos] e sua interpretação. O [valor normalizado] do atributo diferencia maiúsculas de minúsculas e deve ser minúsculo e não pode conter espaço em branco.
[valor normalizado] do AII do Atributo de Tipo JSON | [filhos] permitido do EII correspondente | Mapeamento para JSON |
---|---|---|
string (ou ausência do AII tipo JSON)string e a ausência de AII tipo JSON, que são os mesmos, tornam padrão string .Portanto, <root> string1</root> mapeia para o JSON string "string1". |
0 ou mais CIIs | Um JSON string (JSON RFC, seção 2.5). Cada char um é um caractere que corresponde ao [código de caractere] da CII. Se não houver CIIs, ele será mapeado para um JSON string vazio.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 escapados são mapeados para caracteres de escape. Todos os outros são mapeados para caracteres que não são escapados. O caractere "/" é especial. Ele é escapado mesmo que não precise (escrito como "\/"). Exemplo: o seguinte elemento é mapeado para um fragmento JSON. <root type="string">the "da/ta"</root> O fragmento JSON é "\"da\/ta\"". No mapeamento JSON para XML, qualquer caractere de escape e caracteres que não são escape mapeiam corretamente para o [código de caractere] correspondente. Exemplo: o fragmento JSON "\u0041BC" mapeia para o seguinte elemento XML. <root type="string">ABC</root> A cadeia de caracteres pode ser cercada por espaço em branco (“ws” na seção 2 do JSON RFC) que não é mapeado para 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 o espaço em branco no 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 | Um JSON number (JSON RFC, seção 2.4) provavelmente 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 caractere] da CII.Exemplo: o seguinte elemento é 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 ), provavelmente cercado por CIIs adicionais com espaço em branco. |
Uma sequência cii que corresponde à cadeia de caracteres "true" é mapeada para o literal true e uma sequência CII que corresponde à cadeia de caracteres "false" é mapeada para o literal false . O espaço em branco ao redor é preservado.Exemplo: o seguinte elemento é mapeado para 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 pode ser cercado (“ws” na seção 2) que não é mapeado para XML.Exemplo: o seguinte elemento é mapeado para um fragmento JSON. <root type="null"/> ou <root type="null"></root> : O fragmento JSON em ambos os casos é Null . |
object |
0 ou mais EIIs. | A begin-object (chave esquerda) como na seção 2.2 do JSON RFC, seguido por um registro de membro para cada EII, conforme descrito abaixo. Se houver mais de um EII, haverá separadores de valor (vírgulas) entre os registros de membro. Tudo é seguido por um objeto final (chave direita).Exemplo: o seguinte elemento é mapeado para um 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 está presente no XML mapeado, mas uma EII correspondente não está presente. Observe que esse registro de membro deve ocorrer primeiro no objeto JSON para que esse mapeamento especial seja aplicado. Assim, representa uma diferença do processamento JSON habitual, em que a ordem dos registros de membro 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 um significado especial e mapeia para um EII como de costume, não para AII. As regras de escape/não escape para o [valor normalizado] de AII quando mapeado para um valor JSON são iguais às cadeias de caracteres JSON, especificadas na linha "string" desta tabela. Exemplo: <root type="object" __type="\abc" /> para o exemplo anterior é possível mapear para o JSON a seguir. {"__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 seguinte fragmento JSON é mapeado para 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 > |
array | 0 ou mais EIIs | Uma matriz inicial (colchete esquerdo) como na seção 2.3 do JSON RFC, seguida por um registro de matriz para cada EII, conforme descrito abaixo. Se houver mais de um EII, haverá separadores de valor (vírgulas) entre os registros de matriz. AII é 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 que mapeia. <root type="array"/> <item type="string">aaa</item> <item type="string">bbb</item> </root > |
Os Registros de Membro funcionam:
- O [nome local] do elemento interno é mapeado para a parte
string
domember
conforme definido na seção 2.2 do JSON RFC.
Exemplo: o seguinte elemento é mapeado 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 no JSON são escapados e os outros não são escapados. O caractere "/", embora não seja um caractere de escape, é escapado (ele não precisa ser escapado no mapeamento JSON para XML). É necessário para dar suporte ao formato AJAX em ASP.NET para dados
DateTime
no 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 produza um [nome local].Elementos internos [filhos] mapeiam o valor na seção 2.2, de acordo com
JSON Type Attribute
assim comoRoot JSON Element
. Vários níveis de aninhamento de EIIs (incluindo aninhamento dentro de matrizes) são permitidos.
Exemplo: o seguinte elemento é mapeado 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}}
Observação
Não há nenhuma etapa de codificação XML no mapeamento anterior. Portanto, o WCF apenas oferece suporte a documentos JSON em que todos os caracteres em nomes chave são caracteres válidos em nomes de elemento XML. Por exemplo, não há suporte para o documento JSON {"<:"a"} 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 etapas de escape/não escape de JSON.
Os registros de matriz funcionam da seguinte forma:
O [nome local] do elemento interno é "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, assim como para o elemento raiz JSON. Vários níveis de aninhamento de EIIs (incluindo aninhamento dentro de matrizes) são permitidos.
Exemplo: o seguinte elemento é mapeado 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>
Veja a seguir o fragmento JSON.
["myValue1",2,[true,null]]