Compartir a través de


Mapeo entre JSON y XML

Los sistemas de lectura y escritura generados por el JsonReaderWriterFactory proporcionan una API de XML sobre contenido de notación de objetos JavaScript (JSON) JSON codifica datos mediante un subconjunto de literales de objeto de JavaScript. También se utilizan los lectores y escritores producidos por esta fábrica cuando las aplicaciones de Windows Communication Foundation (WCF) envían o reciben contenido JSON utilizando WebMessageEncodingBindingElement o WebHttpBinding.

Cuando se inicializa con contenido de JSON, el lector de JSON se comporta del mismo modo que un lector XML textual sobre una instancia de XML. El sistema de escritura de JSON, cuando se proporciona una secuencia de llamadas que en un lector XML textual genera una cierta instancia XML, escribe contenido JSON. El mapeo entre esta instancia de XML y el contenido JSON se describe en este documento para su uso en escenarios avanzados.

Internamente, JSON se representa como un conjunto de información XML cuando WCF lo procesa. Normalmente no tiene que preocuparse por esta representación interna, puesto que el mapeo solo es una representación lógica: JSON no se convierte normalmente en XML en memoria ni se transforma desde XML a JSON. La asignación significa que API XML se utilizan para obtener acceso al contenido de JSON.

Cuando WCF usa JSON, el escenario habitual es que DataContractJsonSerializer sea conectado automáticamente mediante el comportamiento WebScriptEnablingBehavior o el comportamiento WebHttpBehavior, cuando sea apropiado. DataContractJsonSerializer entiende la asignación entre JSON y los conjuntos de información XML y actúa como si tratase directamente con JSON. (Es posible utilizar el DataContractJsonSerializer con cualquier sistema de lectura o escritura XML, siempre que se comprenda que el XML cumple la asignación siguiente).

En escenarios avanzados, puede volverse necesario acceder directamente a la siguiente asignación. Estos escenarios se producen cuando desea serializar y deserializar JSON de maneras personalizadas, sin basarse en el DataContractJsonSerializer, o cuando se trata directamente con el tipo Message para mensajes que contienen JSON. La asignación JSON-XML también se utiliza para el registro de mensajes. Al usar la característica de registro de mensajes en WCF, los mensajes JSON se registran como XML de acuerdo con la asignación descrita en la siguiente sección.

Para aclarar el concepto de asignación, se proporciona el siguiente ejemplo, que procede de un documento JSON.

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

Para leer este documento JSON mediante uno de los lectores previamente mencionados, utilice la misma secuencia de llamadas a XmlDictionaryReader que utilizaría para leer el siguiente documento XML.

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

Es más, si WCF recibe el mensaje JSON del ejemplo y lo registra, vería el fragmento XML en el registro anterior.

Mapeo entre JSON y el conjunto de información XML

Formalmente, la asignación se produce entre JSON, tal como se describe en RFC 4627 (excepto con ciertas restricciones relajadas y ciertas otras restricciones añadidas), y el infoset XML (y no XML textual), como se describe en XML Information Set. Consulte este tema para ver las definiciones de los elementos de información y los campos entre [corchetes].

Un documento JSON en blanco se asigna a un documento XML en blanco, y un documento XML en blanco se asigna a un documento JSON en blanco. En la asignación de XML a JSON, no se permiten espacios en blanco al comienzo ni al final del documento.

La asignación se define entre un elemento de información de documento (DII) o un elemento de información de elemento (EII) y JSON. El EII o la propiedad [document element] (elemento de documento) del DII, se conoce como el elemento JSON raíz. Tenga en cuenta que los fragmentos de documentos (XML con varios elementos raíz) no se admiten en este mapeo.

Ejemplo: el documento siguiente:

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

Y el siguiente elemento:

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

Ambos tienen un mapeo a JSON. El elemento <root> es el elemento JSON raíz en ambos casos.

Además, en el caso de un DII, se debería considerar lo siguiente:

  • Algunos elementos en la lista [children] no deben estar presentes. No confíe en este hecho al leer XML mapeado desde JSON.

  • La lista [children] no contiene ningún elemento de información de comentario.

  • La lista [children] no contiene elementos de información de DTD.

  • La lista [children] no conserva elementos de información personal (PI) (la declaración <?xml…> no está considerada como un elemento de información PI).

  • El conjunto [notations] está vacío.

  • El conjunto [unparsed entities] (entidades no analizadas) está vacío.

Ejemplo: el siguiente documento no tiene ningún mapeo a JSON porque [children] contiene un PI y un comentario.

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

El EII del elemento JSON raíz tiene las siguientes características:

  • [local name] (nombre local) tiene el valor "root" (raíz).

  • [nombre de espacio de nombres] no tiene ningún valor.

  • [prefix] (prefijo) no tiene ningún valor.

  • [children] puede contener elementos EII (que representan los elementos internos, tal y como se describe más adelante) o elementos CII (elementos de información de caracteres, tal y como se describe más adelante) o ninguno de éstos, pero no ambos.

  • [attributes] pueden contener los siguientes elementos de información de atributos opcionales (AII)

  • El atributo de tipo JSON ("type"), tal y como se describe más adelante. Este atributo se utiliza para conservar el tipo JSON (cadena, número, booleano, objeto, matriz o nulo) en el XML asignado.

  • El atributo de nombre del contrato de datos ("__type") tal y como se describe más adelante. Este atributo solo puede estar presente si el atributo de tipo de JSON también está presente y su [normalized value] (valor normalizado) es "object". DataContractJsonSerializer utiliza este atributo para conservar información del tipo de contrato de datos; por ejemplo, en casos polimórficos donde se serializa un tipo derivado y donde se espera un tipo base. Si no está trabajando con el DataContractJsonSerializer, en la mayoría de los casos, se pasa por alto este atributo.

  • [in-scope namespaces] contiene el enlace de "xml" a http://www.w3.org/XML/1998/namespace tal y como requiere la especificación del conjunto de información.

  • [children], [attributes] e [in-scope namespaces] no deben tener elementos distintos de los especificados previamente y [namespace attributes] no deben tener ningún miembro, pero no confíe en estos hechos al leer XML asignado a partir de JSON.

Ejemplo: el siguiente documento no tiene ningún mapeo a JSON porque [namespace attributes] no está vacío.

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

El AII para el atributo de tipo JSON tiene las siguientes características:

  • [namespace name] no tiene valor.
  • [prefix] (prefijo) no tiene ningún valor.
  • [local name] es "tipo".
  • [normalized value] es uno de los valores de tipo posibles descritos en la siguiente sección.
  • [specified] es true.
  • [attribute type] (tipo de atributo) no tiene valor.
  • [references] (referencias) no tiene valor.

El AII para el atributo de nombre del contrato de datos tiene las siguientes características:

  • [namespace name] no tiene valor.
  • [prefix] (prefijo) no tiene ningún valor.
  • [local name] es "__type" (dos caracteres de subrayado y, a continuación, "type").
  • [normalized value] (valor normalizado) es cualquier cadena Unicode válida (la asignación de esta cadena a JSON se describe en la siguiente sección).
  • [specified] es true.
  • [attribute type] (tipo de atributo) no tiene valor.
  • [references] no tienen valor.

Los elementos internos contenidos dentro del elemento JSON raíz u otros elementos internos tienen las siguientes características:

  • [local name] puede tener cualquier valor, como se describe más adelante.
  • [namespace name], [prefix], [children], [attributes], [namespace attributes] e [in-scope namespaces] están sujetos a las mismas reglas que el elemento JSON raíz.

En el elemento JSON raíz y los elementos internos, el atributo de tipo JSON define el mapeo a JSON y los posibles elementos hijos y su interpretación. El valor normalizado del atributo distingue entre mayúsculas y minúsculas, debe estar en minúsculas y no puede contener espacios.

El elemento [normalized value] de AII del atributo de tipo JSON Permitidos [children] del EII correspondiente Mapeado a JSON
string (o ausencia del AII de tipo JSON)

Una string y la ausencia del AII de tipo JSON son lo mismo, y hace que string sea el valor predeterminado.

Entonces, <root> string1</root> se asigna al JSON string "string1".
0 o más CII string JSON (RFC de JSON, sección 2.5). Cada char es un carácter que corresponde al [character code] (código de carácter) del CII. Si no hay CIIs, se asigna a un JSON vacío string.

Ejemplo: el elemento siguiente se asigna a un fragmento de JSON:

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

El fragmento de JSON es "42".

En la asignación de XML a JSON, los caracteres que deben escaparse se asignan a caracteres escapados, mientras que todos los demás se asignan a caracteres sin escapar. El carácter "/" es especial: está escapado aunque no es necesario (escrito como "\/").

Ejemplo: El siguiente elemento se mapea a un fragmento de JSON.

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

El fragmento de JSON es "the \"da\/ta\"".

En la asignación de JSON a XML, cualquier carácter con escape y sin escape se asignan correctamente al [character code] correspondiente.

Ejemplo: el fragmento de JSON "\u0041BC" se asigna al siguiente elemento XML.

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

La cadena puede estar rodeada de espacios en blanco ('ws' en la sección 2 de la RFC de JSON) que no se mapean a XML.

Ejemplo: el fragmento de JSON "ABC", (hay espacios delante de la primera comilla doble) se asigna al siguiente elemento XML.

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

Cualquier espacio en blanco de XML se asigna al espacio en blanco en JSON.

Ejemplo: el siguiente elemento XML se asigna a un fragmento de JSON.

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

El fragmento de JSON es " A BC ".
number 1 o más CII Un valor number de JSON (RFC de JSON, sección 2.4), posiblemente incluido entre espacios en blanco. Cada carácter en la combinación de número/espacio en blanco es un carácter que corresponde a [código de carácter] de CII.

Ejemplo: el siguiente elemento mapea a un fragmento de JSON.

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

El fragmento JSON es 42

(Se conserva el espacio en blanco).
boolean 4 o 5 CIIs (que corresponden a true o false), posiblemente rodeados por CIIs adicionales de espacios en blanco. Una secuencia de CII que corresponde a la cadena "true" se asigna al true literal, y una secuencia de CII que corresponde a la cadena "false" se asigna al false literal. Se conserva el espacio en blanco circundante.

Ejemplo: el siguiente elemento se asigna a un fragmento de JSON.

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

El fragmento de JSON es false.
null No se permite ninguno. El literal null. En la asignación de JSON a XML, el valor null puede estar rodeado de espacios en blanco (“ws” en la sección 2) que no se mapean a XML.

Ejemplo: el siguiente elemento mapea a un fragmento de JSON.

<root type="null"/>

o

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

:

El fragmento de JSON es en ambos casos Null.
object 0 o más EII. begin-object (llave izquierda) como en la sección 2.2 del RFC de JSON, seguido de un registro de miembros para cada EII, según se describe más adelante. Si hay más de un EII, hay separadores de valores (comas) entre los registros de los miembros. Todo esto va seguido de un objeto de fin (llave derecha).

Ejemplo: el siguiente elemento se asigna al fragmento JSON.

<root type="object">

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

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

</root >

El fragmento de JSON es {"type1":"aaa","type2":"bbb"}.

Si el atributo de tipo de contrato de datos está presente en el mapeo de XML a JSON, se inserta un registro de miembro adicional al principio. El nombre es el [local name] del atributo de tipo de contrato de datos ("__type"), y su valor es el valor normalizado [normalized value] del atributo. Por el contrario, en la asignación de JSON a XML, si el nombre del registro del primer miembro es el [nombre local] del Atributo del Tipo de Contrato de Datos (es decir, "__type"), habrá un atributo correspondiente de tipo de contrato de datos en el XML mapeado, pero no estará presente un EII correspondiente. Tenga en cuenta que esta entrada de miembro debe aparecer primero en el objeto JSON para que se aplique este mapeo especial. Esto representa una salida del procesamiento de JSON habitual, donde el orden de los registros de miembros no es importante.

Ejemplo:

El siguiente fragmento de JSON se convierte en XML.

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

El XML es el código siguiente.

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

Observe que el __type AII está presente, pero no hay __type EII.

Sin embargo, si se invierte el orden en JSON como se muestra en el ejemplo siguiente.

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

Se muestra el XML correspondiente.

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

Es decir, __type deja de tener un significado especial y se asigna a un EII como es habitual, no a un AII.

Las reglas de escape/no escape para el [normalized value] del AII cuando se asigna a un valor JSON son las mismas que para cadenas JSON, especificadas en el renglón "string" de esta tabla.

Ejemplo:

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

Al ejemplo anterior se puede mapear al siguiente JSON.

{"__type":"\\abc"}

En la conversión de XML a JSON, el [local name] del primer EII no debe ser "__type".

El espacio en blanco (ws) nunca se genera en la asignación de XML a JSON de objetos y se omite en la asignación de JSON a XML.

Ejemplo: El siguiente fragmento de JSON se corresponde con un elemento XML.

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

El elemento XML se muestra en el siguiente código.

<root type="object"> <ccc type="string">aaa</ccc> <ddd type="string">bbb</bar> </root >
matriz 0 o más EII Un inicio de matriz (corchete izquierdo) como en la sección 2.3 del JSON RFC, seguido por un registro de matriz para cada EII, como se describe más adelante. Si hay más de un EII, hay separadores de valores (comas) entre los registros de matrices. Una matriz es seguida por una matriz de final.

Ejemplo: el elemento XML siguiente se mapea a un fragmento de JSON.

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

El fragmento JSON es ["aaa","bbb"].

El espacio en blanco (ws) nunca se genera en la asignación de XML a JSON para matrices y se omite en la asignación de JSON a XML.

Ejemplo: un fragmento JSON.

["aaa", "bbb"]

El elemento XML al que se mapea.

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

Los registros de miembros funcionan de la siguiente manera:

  • El [local name] del elemento interno se asigna a la parte de string del member, según se define en la sección 2.2 del RFC de JSON.

Ejemplo: el siguiente elemento se corresponde con un fragmento de JSON.

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

Se muestra el siguiente fragmento de JSON.

{"myLocalName":"aaa"}
  • En la asignación de XML a JSON, los caracteres que deben escaparse en JSON se escapan, y el resto no se escapan. El carácter "/", aunque no es un carácter que deba ser escapado, lo es de todas formas (no es necesario que sea escapado en la asignación de JSON a XML). Esto es necesario para admitir el formato AJAX de ASP.NET para los datos DateTime en JSON.

  • En la asignación de JSON a XML, todos los caracteres (incluidos los que no tienen escape, si fuese necesario) se toman para formar una string que genera un [local name].

  • Los elementos internos [children] se asignan al valor en la sección 2.2, de acuerdo con el JSON Type Attribute de la misma manera que para el Root JSON Element. Se permiten varios niveles de anidación de EII (incluida la anidación dentro de matrices).

Ejemplo: el siguiente elemento corresponde a un fragmento de 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 >

El siguiente fragmento de JSON es al que se asigna.

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

Nota

No hay ningún paso de codificación XML en el mapeado anterior. Por consiguiente, WCF solo admite documentos JSON donde todos los caracteres en nombres de claves sean caracteres válidos en nombres de elementos XML. Por ejemplo, el documento JSON {"<":"a"} no se admite, porque < no es un nombre válido de un elemento XML.

La situación inversa (caracteres válidos en XML, pero no en JSON) no produce ningún problema, puesto que la asignación anterior incluye pasos de escape/sin escape de JSON.

Los registros de matrices funcionan de la siguiente manera:

  • El nombre local del elemento interno es "item".

  • Los [children] (elementos secundarios) del elemento interno se asignan al valor en la sección 2.3, según el atributo de tipo JSON, de la misma manera que se hace para el elemento JSON raíz. Se permiten varios niveles de anidación de EII (incluida la anidación dentro de objetos).

Ejemplo: el elemento siguiente se asigna a un fragmento de 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 continuación, se muestra el fragmento JSON.

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

Consulte también