¿Le resultó útil esta página?
Sus comentarios sobre este contenido son muy importantes. Háganos saber su opinión.
¿Tiene comentarios adicionales?
Caracteres restantes: 1500
Exportar (0) Imprimir
Expandir todo
Expandir Minimizar

Introducción a JavaScript Object Notation (JSON) en JavaScript y .NET

Febrero de 2007

Publicado: 26 de Junio de 2007

Atif Aziz, Scott Mitchell

Este artículo se aplica a:
    JSON
   Ajax

Resumen: Este artículo analiza JavaScript Object Notation (JSON), un formato de intercambio de datos abierto y basado en texto, que ofrece un intercambio estandarizado de datos, más conveniente para aplicaciones web de estilo Ajax.

Descargar el código de este artículo.

En esta página

Introducción Introducción
Conceptos básicos de la notación literal en JavaScript Conceptos básicos de la notación literal en JavaScript
Comparación de JSON y XML Comparación de JSON y XML
Creación y análisis sintáctico de mensajes JSON con JavaScript Creación y análisis sintáctico de mensajes JSON con JavaScript
Trabajo con JSON en .NET Framework Trabajo con JSON en .NET Framework
Conclusión Conclusión
Referencias Referencias

Introducción

Al diseñar una aplicación que se comunicará con un equipo remoto, se debe seleccionar un protocolo para el formato e intercambio de los datos. Existe una variedad de opciones abiertas y estandarizadas y la elección ideal depende de los requisitos de las aplicaciones y de la funcionalidad preexistente. Por ejemplo, los servicios web basados en SOAP dan formato a los datos en una carga XML contenida en una envoltura SOAP.

Aunque en muchos escenarios de aplicación XML funciona bien, presenta ciertos inconvenientes que hacen que no sea ideal. Las aplicaciones web de estilo Ajax son un espacio en que XML no es ideal. Ajax es una técnica usada para crear aplicaciones web interactivas que ofrecen una experiencia de usuario más ágil mediante el uso de llamadas al servidor web, livianas y fuera de banda, en lugar de devoluciones de página completa. Estas llamadas asincrónicas se inician mediante Javascript en el cliente e implican dar formato a los datos, enviarlos a un servidor web, analizar los datos devueltos y trabajar con éstos. Aunque la mayoría de los exploradores pueden crear, enviar y analizar XML, JavaScript Object Notation (JSON) ofrece un formato estandarizado de intercambio de datos más conveniente para aplicaciones web de estilo Ajax.

JSON es un formato de intercambio de datos abierto y basado en texto (consulte la RFC 4627). Igual que XML, es legible e independiente de la plataforma, además de tener a su disposición una amplia gama de implementaciones. Los datos con formato según el estándar JSON son ligeros y las implementaciones de JavaScript pueden analizarlos sintácticamente con increíble facilidad, lo que lo convierte en el formato ideal de intercambio de datos para aplicaciones web de Ajax. Puesto que JSON es ante todo un formato de datos, no está limitado a las aplicaciones web de Ajax y prácticamente se puede usar en cualquier escenario en que las aplicaciones necesiten intercambiar o almacenar información estructurada como texto.

Este artículo examina el estándar JSON, su relación con JavaScript y en qué se distingue de XML. Se analiza Jayrock, una implementación JSON de código abierto para .NET, y se proporcionan ejemplos de creación y análisis de mensajes JSON en JavaScript y C#.

Conceptos básicos de la notación literal en JavaScript

Los literales se usan en los lenguajes de programación para expresar literalmente valores fijos, como el valor entero constante 4 o la cadena "Hello, World". Los literales se pueden usar en la mayoría de los lenguajes dondequiera que se permita una expresión, como por ejemplo, en la parte de la condición de una instrucción de control, en un parámetro de entrada cuando se llama a una función, en la asignación de variables, etc. Por ejemplo, el siguiente código en C# y Visual Basic inicializa la variable X con el valor entero constante 42.

int x = 42;  // C#
Dim x As Integer = 42  ' Visual Basic

Los distintos lenguajes de programación permiten tipos de literales diferentes. La mayoría de los lenguajes de programación admiten, como mínimo, literales para tipos escalares como por ejemplo enteros, números de punto flotante, cadenas y booleanos. Lo interesante de JavaScript es que además de tipos escalares, admite también literales para tipos estructurados como matrices y objetos. Esta característica permite una sintaxis concisa para la creación e inicialización de matrices y objetos a petición.

Los literales de matriz en JavaScript están compuestos de cero o más expresiones, donde cada expresión representa un elemento de la matriz. Los elementos de la matriz se encierran entre corchetes ([ ]) y se delimitan con comas. El siguiente ejemplo define literalmente una matriz con siete elementos de cadena que contienen los nombres de los siete continentes:

var continents = ["Europe", "Asia", "Australia", "Antarctica", "North
 America", "South America", "Africa"];
alert(continents[0] + " is one of the " + continents.length + "
 continents.");

Ahora compárelo con la forma en que crearía e inicializaría una matriz en JavaScript sin la notación literal:

var continents = new Array();
continents[0] = "Europe";
continents[1] = "Asia";
continents[2] = "Australia";
continents[3] = "Antarctica";
continents[4] = "North America";
continents[5] = "South America";
continents[6] = "Africa";

Un objeto literal define los miembros de un objeto y sus valores. La lista de miembros y valores del objeto se encierran entre llaves ({}) y cada miembro está delimitado por una coma. Dentro de cada miembro, el nombre y el valor se delimitan con dos puntos (:). En el ejemplo siguiente se crea un objeto y se inicializa con tres miembros denominados Address, City y PostalCode con sus respectivos valores "123 Anywhere St.","Springfield" y "99999".

var mailingAddress = { 
     "Address"    :   "123 Anywhere St.", 
     "City"       :   "Springfield", 
     "PostalCode" :   99999
};
alert("The package will be shipped to postal code " +
 mailingAddress.PostalCode);

Los ejemplos presentados hasta ahora ilustran el uso de literales de cadena y numéricos dentro de literales de matriz y de objeto. También se puede expresar un gráfico completo mediante uso recursivo de la notación, de manera tal que los valores de los elementos de matriz y de miembros de objetos puedan, a su vez, usar literales de objetos y de matriz. Por ejemplo, el siguiente fragmento de código ilustra un objeto que tiene una matriz como miembro (PhoneNumbers), donde la matriz está compuesta por una lista de objetos.

var contact = {
     "Name": "John Doe",
     "PermissionToCall": true,
     "PhoneNumbers": [ 
       {
           "Location": "Home",
           "Number": "555-555-1234"
       },
       {
           "Location": "Work",
           "Number": "555-555-9999 Ext. 123"
       }
     ]
};
if (contact.PermissionToCall)
{
  alert("Call " + contact.Name + " at " + contact.PhoneNumbers[0].Number);
}

Nota

Para obtener un análisis más completo de compatibilidad de literales con JavaScript consulte la sección Literales de la Guía principal de Javascript 1.5.

De literales de JavaScript a JSON

JSON es un formato de intercambio de datos creado a partir de un subconjunto de la notación de literales de objetos en JavaScript. Aunque JavaScript acepta una sintaxis para valores literales muy flexible, es importante tener en cuenta que JSON posee reglas mucho más estrictas. Según el estándar JSON, por ejemplo, el nombre de un miembro de objeto debe ser una cadena JSON válida. En JSON una cadena se debe encerrar entre comillas. Por otra parte, JavaScript, permite delimitar nombres de miembros de objeto con comillas o apóstrofes u omitirlas por completo siempre que el nombre de miembro no provoque conflictos con una palabra clave reservada de JavaScript. De igual modo, en JSON el valor de un elemento de matriz o de miembro de objeto está limitado a un conjunto muy limitado. Sin embargo, en JavaScript, los valores de elementos de matriz y miembros de objeto pueden referirse prácticamente a cualquier expresión válida de JavaScript, como llamadas de función y definiciones.

Los bueno de JSON es su sencillez. Un mensaje con formato según el estándar JSON está compuesto de un único objeto o matriz de nivel superior. Los valores de elementos de matriz y objetos pueden ser objetos, matrices, cadenas, números, valores booleanos (verdadero y falso) o nulo. En definitiva, el estándar JSON es esto. Realmente es así de sencillo. Consulte http://www.json.org/ o la RFC 4627 para obtener una descripción más formal del estándar.

Uno de los puntos delicados de JSON es la falta de un literal de fecha/hora. Muchas personas se sorprenden y decepcionan al enterarse de esto la primera vez que usan JSON. La razón sencilla (si sirve de consuelo) que explica la ausencia de un literal de fecha/hora es que JavaScript tampoco ha tenido nunca: En JavaScript, la compatibilidad con valores de fecha y hora se consigue totalmente a través del objeto Date. Por lo tanto, la mayoría de las aplicaciones que usan JSON como formato de datos, tienden generalmente a usar una cadena o un número para expresar valores de fecha y hora. Si se usa una cadena, lo normal es que esté en formato ISO 8601. Si, en su lugar, se usa un número, entonces el valor generalmente significa el número de milisegundos en Horario universal coordinado (UTC) desde el tiempo base, donde el tiempo base se define como la medianoche del 1 de enero de 1970 (UTC). Nuevamente, es una mera convención y no forma parte del estándar JSON. Si intercambia datos con otra aplicación, necesitará comprobar su documentación para consultar cómo codifica los valores de fecha y hora dentro de un literal JSON. Por ejemplo, AJAX de ASP.NET de Microsoft no usa ninguna de las convenciones descritas. Por el contrario, codifica valores DateTime de .NET como una cadena JSON, donde el contenido de la cadena es \/Date(ticks)\/ y ticks representa los milisegundos desde el tiempo base (UTC). Es decir que el 29 de noviembre de 1989, a las 4:55:30 a. m., en UTC se codifica como "\/Date(628318530718)\/".

AJAX de ASP.NET: Interior de la cadena de fecha y hora de JSON

El serializador JSON de AJAX en ASP.NET codifica una instancia DateTime como cadena JSON. Durante sus ciclos preliminares, AJAX de ASP.NET usó el formato "@ticks@", donde ticks representa el número de milisegundos desde el 1 de enero de 1970 en Horario universal coordinado (UTC). Una fecha y hora en UTC como el 29 de noviembre de 1989, a las 4:55:30 a. m. se escribiría como "@62831853071@". Aunque es simple y sencillo, este formato no puede distinguir un valor serializado de fecha y hora de una cadena que se parezca a una fecha serializada pero que no esté pensada para ser deserializada como tal. Consecuentemente, el equipo de AJAX de ASP.NET hizo un en la versión final para solucionar este problema al adoptar el formato "\/Date(ticks)\/".

El nuevo formato depende de un pequeño truco para reducir la posibilidad de una mala interpretación. En JSON, un carácter de barra diagonal (/) en una cadena se puede escapar con una barra diagonal inversa (\) aunque estrictamente no sea necesario. Para aprovechar esta situación, el equipo de AJAX de ASP.NET modificó JavaScriptSerializer para escribir en su lugar una instancia de DateTime como la cadena "\/Date(ticks)\/". El escape de las dos barras diagonales es superficial, pero importante para JavaScriptSerializer. Según las reglas de JSON "\/Date(ticks)\/" es técnicamente equivalente a "/Date(ticks)/" pero JavaScriptSerializer deserializará la primera como DateTime y la última como String. Por lo tanto, las posibilidades de ambigüedad son considerablemente menores cuando se comparan con el formato más sencillo "@ticks@" de las versiones preliminares.

Comparación de JSON y XML

Tanto JSON como XML se pueden usar para representar objetos nativos en memoria en un formato de intercambio de datos basado en texto y legible. Además, los dos formatos de intercambio de datos son isomorfos: dado un texto en un formato, se puede generar uno equivalente en el otro. Por ejemplo, al llamar a uno de los servicios web públicamente accesibles de Yahoo!, puede indicar a través de un parámetro querystring si se debe dar formato a la respuesta como XML o JSON. Por lo tanto, decidir sobre un formato de intercambio de datos, no es tan sencillo como elegir uno y descartar el otro, sino que se debe determinar qué formato tiene las características que lo convierten en la mejor opción para una aplicación particular. Por ejemplo, XML tiene sus raíces en el marcado de texto en documentos y tiende a destacarse muy bien en ese entrono (como resulta evidente con XHTML). Por otra parte, JSON tiene sus raíces en tipos y estructuras de lenguajes de programación y ofrece, por lo tanto, una asignación más natural y directamente disponible para intercambiar datos estructurados. Más allá de estos dos puntos de partida, la siguiente tabla lo ayudará a entender y comparar las características clave de XML y JSON.

Diferencias de características clave entre XML y JSON

Característica

XML

JSON

Tipos de datos

No ofrece ninguna noción de tipos de datos. Hay que usar el esquema XML para agregar información de tipos.

Ofrece tipos de datos escalares y la capacidad de expresar datos estructurados a través de matrices y objetos.

Compatibilidad con matrices

Las matrices se deben expresar mediante convenciones, por ejemplo a través del uso de un elemento marcador de posición exterior que modela los contenidos de las matrices como elementos interiores. Típicamente, el elemento exterior usa el plural del nombre que se usa en los elementos interiores.

Compatibilidad con matrices nativas.

Compatibilidad con objetos

Los objetos se deben expresar mediante convenciones, con frecuencia a través del uso combinado de atributos y elementos

Compatibilidad con objetos nativos.

Compatibilidad con nulos

Requiere el uso de xsi:nil en elementos de una instancia del documento XML más una importación del espacio de nombres correspondiente.

Reconoce el valor null de forma nativa.

Comentarios

Compatibilidad nativa y generalmente disponible por medio de las API.

No compatible.

Espacios de nombres

Admite espacios de nombres, que eliminan el riesgo de colisiones de nombres al combinar documentos. Los espacios de nombres también permiten extender sin riesgos los estándares existentes basados en XML.

Sin concepto de espacios de nombres. Los conflictos para asignar nombres se evitan generalmente mediante el anidado de objetos o el uso de un prefijo en el nombre de un miembro de objeto (en la práctica, se prefiere lo primero).

Decisiones de formato

Complejas. Se requiere un mayor esfuerzo para decidir cómo asignar tipos de aplicación a elementos y atributos XML. Puede generar muchas discusiones sobre si es preferible un enfoque centrado en elementos o en atributos.

Sencillas. Proporciona una asignación mucho más directa para los datos de aplicación. La única excepción puede ser la ausencia del literal de fecha/hora.

Tamaño

Los documentos tienden a tener gran tamaño, especialmente cuando se usa para el formato el enfoque centrado en elementos.

La sintaxis es muy concisa y da como resultado texto con formato en el que la mayor parte del espacio (con toda la razón) lo consumen los datos representados.

Análisis en JavaScript

Se necesita una implementación de XML DOM y código de aplicación adicional para volver a asignar texto en objetos JavaScript.

No se necesita código de aplicación adicional para analizar texto; se puede usar la función eval de JavaScript.

Curva de aprendizaje

Generalmente tiende a necesitar del uso de varias tecnologías en colaboración: XPath, esquema XML, XSLT, espacios de nombres XML, DOM, etc.

Pila de tecnología muy sencilla conocida por los desarrolladores con experiencia en JavaScript u otros lenguajes de programación dinámicos.

JSON es un formato de intercambio de datos relativamente nuevo y no posee los años de adopción ni el soporte de proveedores que XML disfruta hoy (aunque JSON se está poniendo rápidamente al día). La siguiente tabla resalta el estado actual de asuntos en los espacios XML y JSON.

JSON es un formato de intercambio de datos relativamente nuevo y no posee los años de adopción ni el soporte de proveedores que XML disfruta hoy (aunque JSON se está poniendo rápidamente al día). La siguiente tabla resalta el estado actual de asuntos en los espacios XML y JSON.

Diferencias de compatibilidad entre XML y JSON

Compatibilidad

XML

JSON

Herramientas

Cuenta con un completo conjunto de herramientas que se consiguen con facilidad de muchos proveedores de la industria

La compatibilidad con herramientas variadas (por ejemplo editores y formateadores) es escasa.

Microsoft .NET Framework

Gran y completa compatibilidad desde la versión 1.0 de .NET Framework. La compatibilidad XML está disponible como parte de la biblioteca de clases base (BCL). Para entornos no administrados, existe MSXML

Ninguna hasta ahora, con excepción de una implementación inicial como parte de AJAX de ASP.NET.

Plataforma y lenguaje

Existen muchos analizadores y formateadores disponibles para un gran número de plataformas y lenguajes (implementaciones comerciales y de código abierto).

Los analizadores y formateadores ya se encuentran disponibles en muchas plataformas y en varios lenguajes. Consulte json.org para obtener una amplia serie de referencias. La mayoría de las implementaciones tienden a ser por ahora proyectos de código abierto.

Lenguaje integrado

Actualmente los proveedores de la industria experimentan con compatibilidad dentro de los lenguajes (literalmente). Consulte el proyecto LINQ de Microsoft para obtener más información.

Sólo es compatible de forma nativa en JavaScript/ECMAScript.

Nota

Ninguna tabla tiene como objeto ser una lista completa de puntos de comparación. Existen otros aspectos desde los cuáles se pueden comparar ambos formatos de datos, pero pensamos que estos puntos clave son suficientes para crear una impresión inicial.

Creación y análisis sintáctico de mensajes JSON con JavaScript

Cuando se usa JSON como formato de intercambio de datos, dos tareas habituales son transformar una representación nativa y en memoria en su representación de texto JSON y viceversa. Desafortunadamente, en el momento de redactar este artículo, JavaScript no ofrece funciones integradas para crear texto JSON a partir de un objeto o matriz dados. Se espera que estos métodos se incluyan en la cuarta edición del estándar ECMAScript en 2007. Hasta que estas funciones de formato JSON se agreguen formalmente a JavaScript y se consigan con facilidad en implementaciones populares, use la secuencia de comandos de implementación de referencia disponible para su descarga en http://www.json.org/json.js.

En su última iteración en el momento de redactar este artículo, la secuencia de comandos json.js en http://www.json.org agrega las funciones toJSONString() a matrices, cadenas, booleanos, objetos y otros tipos JavaScript. Las funciones toJSONString() para tipos escalares (como números y booleanos) son bastante sencillas porque sólo necesitan devolver una representación de cadena del valor de la instancia. Por ejemplo, la función toJSONString() para el tipo Booleano, devuelve la cadena "true" si el valor es verdadero y "false" en caso contrario. Las funciones toJSONString() para los tipos Matriz y Objeto son más interesantes. Para instancias de Matriz, la función toJSONString() de cada elemento contenido se llama en secuencia, y los resultados se concatenan con comas para delimitar cada resultado. El resultado final se encierra entre corchetes. De igual modo, para instancias de Objeto, se enumera cada miembro y se invoca su función toJSONString(). El nombre del miembro y la representación JSON de su valor se concatenan entre sí mediante dos puntos; cada par formado por nombre de miembro y valor se delimita con una coma y el resultado completo se encierra entre llaves.

El resultado neto de las funciones toJSONString() es que cualquier tipo se puede convertir en su formato JSON con una única llamada a la función. El siguiente JavaScript crea un objeto de Matriz y agrega deliberadamente siete elementos de Cadena mediante el método detallado y no literal con fines ilustrativos. Continúa mostrando la representación JSON de la matriz:

// josn.js must be included prior to this point

var continents = new Array();
continents.push("Europe");
continents.push("Asia");
continents.push("Australia");
continents.push("Antarctica");
continents.push("North America");
continents.push("South America");
continents.push("Africa");

alert("The JSON representation of the continents array is: " +
 continents.toJSONString());

Figura 1. La función toJSONString () emite la matriz con formato según el estándar JSON.

El análisis de texto JSON resulta aún más sencillo. Como JSON es solamente un subconjunto de literales JavaScript, se puede analizar en una representación en memoria que usa la función eval(expr), y trata al texto JSON de la fuente como código fuente JavaScript. La función eval acepta una cadena de código JavaScript válida como entrada y evalúa la expresión. En consecuencia, la única línea de código siguiente es todo lo que se necesita para transformar texto JSON en una representación nativa:

var value = eval( "(" + jsonText + ")" );

Nota

Los paréntesis adicionales que se usan hacen que eval trate incondicionalmente la entrada de la fuente como una expresión. Esto es importante especialmente con objetos. Si intenta llamar a eval con una cadena que contenga texto JSON que define un objeto, como por ejemplo la cadena"{}" (lo cuál significa un objeto vacío), simplemente devuelve indefinido como resultado analizado. Los paréntesis fuerzan al analizador JavaScript a consultar las llaves de nivel superior como notación literal de una instancia de Objeto antes que, por ejemplo, las llaves que definen un bloque de instrucciones. Por cierto, el mismo problema no ocurre si el elemento superior es una matriz, como en eval (" [1,2,3]”). No obstante, para la uniformidad, el texto JSON se debe encerrar siempre entre paréntesis antes de llamar a eval para que no exista ambigüedad sobre cómo interpretar la fuente.

Al evaluar la notación literal, se devuelve una instancia que corresponde a la sintaxis literal y se asigna un valor. Considere el siguiente ejemplo, que usa la función eval para analizar la notación literal de una matriz y asignar la matriz resultante a la variable continents.

var arrayAsJSONText = '["Europe", "Asia", "Australia", "Antarctica",
 "North America", "South America", "Africa"]';
var continents = eval( arrayAsJSONText );
alert(continents[0] + " is one of the " + continents.length + "
 continents.");

Por supuesto, en la práctica el texto JSON evaluado vendrá de alguna fuente externa en lugar de codificarse como en el caso anterior.

La función eval evalúa ciegamente cualquier expresión que se le pase. Una fuente que no es de confianza, podría por lo tanto incluir JavaScript potencialmente peligroso junto con o mezclado en la notación literal que componen los datos JSON. En escenarios donde no se puede confiar en la fuente, resulta muy recomendable que analice el texto JSON mediante la función parseJSON() (se encuentra en json.js):

// Requires json.js
var continents = arrayAsJSONText.parseJSON();

La función parseJSON() también usa eval, pero sólo si la cadena contenida en arrayAsJSONText cumple el estándar de texto JSON. Lo realiza mediante una prueba inteligente de expresiones regulares.

Trabajo con JSON en .NET Framework

El texto JSON se puede crear y analizar fácilmente con código JavaScript, y éste es uno de sus atractivos. Sin embargo, cuando JSON se usa en una aplicación web ASP.NET, sólo el explorador goza de compatibilidad con JavaScript porque el código en el servidor probablemente está escrito en Visual Basic o C#.

La mayoría de las bibliotecas Ajax diseñadas para ASP.NET ofrecen compatibilidad para la creación y el análisis de texto JSON mediante programación. Por lo tanto, para trabajar con JSON en una aplicación .NET, considere la posibilidad de usar alguna de estas bibliotecas. Existe una gran cantidad de código abierto y opciones de terceros, y Microsoft también dispone de su propia biblioteca Ajax denominada AJAX de ASP.NET.

En este artículo veremos ejemplos que usan Jayrock, una implementación de código abierto de JSON para Microsoft .NET Framework creada por el coautor Atif Aziz. Hemos optado por usar Jayrock en vez de AJAX de ASP.NET por tres razones:

  • Jayrock es código abierto, lo que permite su ampliación o personalización según sea necesario.

  • Jayrock se puede usar en aplicaciones ASP.NET 1.x, 2.0 y Mono, mientras que AJAX de ASP.NET es sólo para la versión de ASP.NET 2.0.

  • El ámbito de Jayrock se limita a JSON y JSON-RPC, y el primero es el tema principal de este artículo. Mientras que AJAX de ASP.NET incluye compatibilidad para la creación y análisis de texto JSON, su propósito principal es ofrecer una plataforma enriquecida para la creación de aplicaciones web de estilo Ajax de un extremo a otro en ASP.NET. Los accesorios adicionales pueden distraer cuando el centro principal de atención es JSON.

El trabajo con JSON en .NET mediante Jayrock es semejante a trabajar con XML mediante las clases XmlWriter, XmlReader y XmlSerializer en .NET Framework. Las clases JsonWriter, JsonReader, JsonTextWriter y JsonTextReader que se encuentran en Jayrock imitan la semántica de las clases XmlWriter, XmlReader, XmlTextWriter y XmlTextReader de .NET Framework. Estas clases son útiles para comunicarse con JSON a un nivel bajo orientado a flujo. Mediante estas clases, se puede crear o analizar por partes texto JSON a través de una serie de llamadas a métodos. Por ejemplo, mediante el método de la clase JsonWriter, WriteNumber (number) escribe la representación apropiada de cadena number según el estándar JSON. La clase JsonConvert ofrece los métodos Export e Import para la conversión entre tipos .NET y JSON. Estos métodos ofrecen una funcionalidad semejante a la que se encuentra en los métodos Serialize y Deserialize, respectivamente de la clase XmlSerializer.

Creación de texto JSON

El código siguiente ilustra el uso de la clase JsonTextWriter para crear el texto JSON de una matriz de cadenas de continentes. Este texto JSON se envía a una instancia de TextWriter pasada al constructor, que en este ejemplo resulta ser el flujo de salida de la consola (en ASP.NET se puede usar en su lugar Response.Output):

using (JsonTextWriter writer = JsonTextWriter(Console.Out))
{
    writer.WriteStartArray();
    writer.WriteString("Europe");
    writer.WriteString("Asia");
    writer.WriteString("Australia");
    writer.WriteString("Antarctica");
    writer.WriteString("North America");
    writer.WriteString("South America");
    writer.WriteString("Africa");
    writer.WriteEndArray();
}

Además de los métodos WriteStartArray, WriteString y WriteEndArray, la clase JsonWriter ofrece los métodos para escribir otros tipos de valores de JSON, tales como WriteNumber, WriteBoolean, WriteNull, etc. Los métodos WriteStartObject, WriteEndObject y WriteMember crean el texto JSON para un objeto. El ejemplo siguiente ilustra la creación de texto JSON para el objeto del contacto examinado en la sección "Descripción de Notación literal en JavaScript":

private static void WriteContact()
{
    using (JsonWriter writer = new JsonTextWriter(Console.Out))
    {
        writer.WriteStartObject();              //  {
        writer.WriteMember("Name");             //      "Name" : 
        writer.WriteString("John Doe");         //          "John Doe",
        writer.WriteMember("PermissionToCall"); //      "PermissionToCall"
 :
        writer.WriteBoolean(true);              //          true,
        writer.WriteMember("PhoneNumbers");     //      "PhoneNumbers" :
        writer.WriteStartArray();               //          [ 
        WritePhoneNumber(writer,                //            {
 "Location": "Home",
            "Home", "555-555-1234");            //              "Number":
 "555-555-1234" },
        WritePhoneNumber(writer,                //            {
 "Location": "Work",
            "Work", "555-555-9999 Ext. 123");   //              "Number":
 "555-555-9999 Ext. 123" }
        writer.WriteEndArray();                 //          ]
        writer.WriteEndObject();                //  }
    }
}
private static void WritePhoneNumber(JsonWriter writer, string location,
 string number)
{
    writer.WriteStartObject();      //  {
    writer.WriteMember("Location"); //      "Location" : 
    writer.WriteString(location);   //          "...", 
    writer.WriteMember("Number");   //      "Number" :
    writer.WriteString(number);     //          "..."
    writer.WriteEndObject();        //  }
}

Los métodos Export y ExportToString de la clase JsonConvert se pueden usar para serializar un tipo especificado de .NET en texto JSON. Por ejemplo, en lugar de construir manualmente el texto JSON para la matriz de los siete continentes que usan la clase JsonTextWriter, la siguiente llamada a Jsonconvert.Exporttostring produce los mismos resultados:

string[] continents = {
      "Europe", "Asia", "Australia", "Antarctica", "North America", "South
 America", "Africa"
};
string jsonText = JsonConvert.ExportToString(continents);

Análisis de texto JSON

La clase JsonTextReader ofrece una variedad de métodos para analizar las muestras de texto JSON y la principal es Read. Cada vez que se invoca el método Read, el analizador consume la muestra siguiente, que podría ser el valor de una cadena, el valor de un número, el nombre del miembro de un objeto, el inicio de una matriz, etc. Cuando proceda, el acceso al texto analizado de la muestra actual se puede realizar a través de la propiedad Text. Por ejemplo, si el lector trabaja con datos booleanos, la propiedad Text devolverá "true" o "false" en función del valor real de análisis.

El código de ejemplo siguiente usa la clase JsonTextReader para analizar la representación de texto JSON de una matriz de cadenas que contiene los nombres de los siete continentes. Cada continente que empieza con la letra "A" se envía a la consola:

string jsonText = @"[""Europe"", ""Asia"", ""Australia"", ""Antarctica"",
 ""North America"", ""South America"", ""Africa""]";

using (JsonTextReader reader = new JsonTextReader(new
 StringReader(jsonText)))
{
    while (reader.Read())
    {
        if (reader.TokenClass == JsonTokenClass.String &&
            reader.Text.StartsWith("A"))
        {
            Console.WriteLine(reader.Text);
        }
    }
}

Nota

La clase JsonTextReader en Jayrock es un analizador de texto JSON bastante libre. De hecho, permite mucha más sintaxis que la que se considera texto válido de JSON según las reglas presentadas en RFC 4627. Por ejemplo, la clase JsonTextReader permite que los comentarios de una línea y varias líneas aparezcan dentro del texto JSON como se esperaría en JavaScript. Los comentarios de una línea comienzan con doble barra diagonal (//) y los de varias líneas con barra diagonal-asterisco (/*) y finalizan con asterisco-barra diagonal (*/). Los comentarios de una sola línea pueden comenzar incluso con el signo hash/libra (#), que es común en los archivos de configuración de estilo Unix. En todas las instancias, el analizador omite completamente los comentarios y nunca los expone a través de la API. También como en JavaScript, JsonTextReader permite delimitar una cadena JSON con un apóstrofe ('). El analizador puede incluso tolerar una coma adicional después del último miembro de un objeto o elemento de una matriz.

Aún con todas estas adiciones, JsonTextReader es un analizador que cumple las expectativas. JsonTextWriter, por otra parte, genera sólo texto JSON que cumple de forma estricta con el estándar. Esto va en consonancia con lo que a menudo se denomina principio de coherencia, "Ser conservador en lo que uno hace; ser liberal en lo que se acepta de los demás".

Para convertir directamente texto JSON en un objeto .NET, use el método import de la clase JsonConvert, especificando el tipo de salida y texto JSON. El ejemplo siguiente muestra la conversión de una matriz JSON de cadenas en una matriz de cadenas .NET:

string jsonText = @"[""Europe"", ""Asia"", ""Australia"", ""Antarctica"",
 ""North America"", ""South America"", ""Africa""]";

string[] continents = (string[]) JsonConvert.Import(typeof(string[]),
 jsonText);

Éste es un ejemplo más interesante de conversión que toma una fuente RSS XML, la deserializa en un tipo .NET mediante XmlSerializer y convierte el objeto en texto JSON mediante JsonConvert (convirtiendo de forma eficaz RSS en XML a texto JSON):

XmlSerializer serializer = new XmlSerializer(typeof(RichSiteSummary));
RichSiteSummary news;

// Get the MSDN RSS feed and deserialize it...

using (XmlReader reader = XmlReader.Create("http://msdn.microsoft.com/rss.xml"))
{
    news = (RichSiteSummary) serializer.Deserialize(reader);
}


// Export the RichSiteSummary object as JSON text, emitting the output to
 Console.Out

using (JsonTextWriter writer = new JsonTextWriter(Console.Out))
{
    JsonConvert.Export(news, writer);
}

Nota

La definición de RichSiteSummary y sus tipos relacionados se puede encontrar en los ejemplos que acompañan a este artículo.

Uso de JSON en ASP.NET

Después de analizar las formas de trabajar con JSON en JavaScript y desde .NET Framework mediante Jayrock, es tiempo de pasar a un ejemplo práctico sobre dónde y cómo se puede aplicar todo este conocimiento. Considere la característica de devolución de llamada de secuencia de comandos de cliente en ASP.NET 2.0, que simplifica el proceso de realización de llamadas fuera de banda desde el explorador web a la página ASP.NET (o a un control particular en la página). Durante un escenario típico de devolución de llamada, la secuencia de comandos del cliente en el explorador crea los paquetes y envía los datos de regreso al servidor web para que un método del servidor los procese. Después de recibir del servidor los datos de respuesta, el cliente los usa para actualizar la pantalla del explorador.

Nota

Se puede encontrar más información en el artículo de MSDN Magazine Devoluciones de llamadas de secuencias de comandos en ASP.NET 2.0.

El desafío en un escenario de devolución de llamada de cliente es que el cliente y el servidor sólo pueden enviar una cadena de un lado al otro. Por lo tanto, la información que se va a intercambiar se debe convertir antes de enviarla, de una representación nativa en memoria a una cadena y, a continuación, se debe analizar desde una cadena y regresar a su representación nativa en memoria cuando se recibe. La característica de devolución de llamada de secuencia de comandos de cliente en ASP.NET 2.0 no requiere un formato particular de cadena para los datos intercambiados, ni ofrece ninguna funcionalidad integrada para conversión entre las representaciones nativas en memoria y de cadena; depende del desarrollador implementar la lógica de conversión basada en algún formato de intercambio de datos de su elección.

El ejemplo siguiente ilustra cómo usar JSON como formato de intercambio de datos en un escenario de devolución de llamada de secuencia de comandos de cliente. En particular, el ejemplo consiste de una página ASP.NET que usa los datos de la base de datos Northwind para proporcionar una lista de las categorías en una lista desplegable; los productos en la categoría seleccionada se muestran en una lista con viñetas (consulte la figura 3). Siempre que se cambia la lista desplegable en el cliente, se hace una devolución de llamada al pasar una matriz cuyo único elemento es el CategoryID seleccionado.

Nota

Pasamos una matriz que contiene el CategoryID seleccionado como su único elemento (en lugar de CategoryID) porque el estándar JSON requiere que cualquier texto JSON tenga un objeto o una matriz como su raíz. Por supuesto, el cliente no necesita pasar texto JSON al servidor: podríamos haber hecho que este ejemplo sólo pasase el CategoryID seleccionado como una cadena. Sin embargo, quisimos demostrar el envío de texto JSON en ambos mensajes, de solicitud y de respuesta de la devolución de llamada.

El código siguiente en el controlador de eventos Page_Load configura el control web Categories DropDownList para que cuando cambie, llame a la función GetProductsForCategory y pase el valor de las listas desplegables seleccionadas. Esta función inicia la devolución de llamada de secuencia de comandos de cliente si el valor de la lista desplegable pasado es mayor que cero:

// Add client-side onchange event to drop-down list
Categories.Attributes["onchange"] = "Categories_onchange(this);";

// Generate the callback script
string callbackScript = ClientScript.GetCallbackEventReference(
    /* control        */ this, 
    /* argument       */ "'[' + categoryID + ']'", 
    /* clientCallback */ "showProducts", 
    /* context        */ "null");

// Add the Categories_onchange function
ClientScript.RegisterClientScriptBlock(GetType(),
"Categories_onchange", @"
    function Categories_onchange(sender)
    {
        clearResults();

        var categoryID = sender.value;            
        if (categoryID > 0)
        {
            " + callbackScript + @"
        }
    }", true);

El método GetCallBackEventReference en la clase ClientScriptManager, que se usa para crear el código JavaScript que invoca la devolución de llamada, tiene la firma siguiente:

public string GetCallbackEventReference (
    Control control,
    string argument,
    string clientCallback,
    string context,
)

El parámetro argument especifica qué datos se envían del cliente al servidor web durante la devolución de llamada y el parámetro clientCallback especifica el nombre de la función que se debe invocar en el cliente al finalizar la devolución de llamada (showProducts). La llamada al método GetCallBackEventReference crea el código JavaScript siguiente y lo agrega al marcado representado:

WebForm_DoCallback('__Page','[' + categoryID + 
']',showProducts,null,null,false)

'[' + categoryID + ']' es el valor que se pasa al servidor durante la devolución de llamada (una matriz con un único elemento, categoryID) y showProducts es la función de JavaScript que se ejecuta en el retorno de la devolución de llamada.

En el servidor, el método que se ejecuta en respuesta a la devolución de llamada usa la clase JsonConvert de Jayrock para analizar el texto JSON entrante y dar formato el texto JSON saliente. En particular, los nombres de los productos asociados a la categoría seleccionada se recuperan y devuelven como una matriz de cadenas.

// Deserialize the JSON text into an array of integers
int[] args = (int[]) JsonConvert.Import(typeof(int[]), eventArgument);

// Read the selected CategoryID from the array
int categoryID = args[0];

// Get products based on categoryID 
NorthwindDataSet.ProductsRow[] rows = Northwind.Categories.FindByCategoryID(categoryID).GetProductsRows();

// Load the names into a string array
string[] productNames = new string[rows.Length];
for (int i = 0; i < rows.Length; i++)
{
    productNames[i] = rows[i].ProductName;
}

// Serialize the string array as JSON text and return it to the client
return JsonConvert.ExportToString(productNames);

Nota

La clase JsonConvert se usa dos veces: una vez para convertir el texto JSON en eventArgument en una matriz de enteros y a continuación para convertir la matriz de cadenas productNames en texto JSON para devolver al cliente. También podríamos haber usado aquí las clases JsonReader y JsonWriter, pero JsonConvert hace bastante bien el mismo trabajo cuando los datos implicados son relativamente pequeños y se asignan con facilidad a tipos existentes.

Cuando los datos se devuelven desde el servidor, se llama a la función de JavaScript especificada en el método GetCallBackEventReference y se pasa el valor devuelto. Este método showProducts de JavaScript, comienza por hacer referencia al elemento <div> ProductOutput. A continuación analiza la respuesta de JSON y agrega dinámicamente una lista desordenada con un elemento de lista por cada elemento de matriz. Si no se devuelven productos en la categoría seleccionada, entonces se muestra en su lugar un mensaje correspondiente.

function showProducts(arg, context)
{
    // Dump the JSON text response from the server.

    document.forms[0].JSONResponse.value = arg;
    
    // Parse JSON text returned from callback.

    var categoryProducts = eval("(" + arg + ")");

    // Get a reference to the <div> ProductOutput.
    
    var output = document.getElementById("ProductOutput");

    // If no products for category, show message.
    
    if (categoryProducts.length == 0)
    {
        output.appendChild(document.createTextNode("There are no products
 for this category..."));
    }
    else
    {
        // There are products, display them in an unordered list. 
        
        var ul = document.createElement("ul");
        
        for (var i = 0; i < categoryProducts.length; i++)
        {
            var product = categoryProducts[i];
            var li = document.createElement("li");
            li.appendChild(document.createTextNode(product));
            ul.appendChild(li);
        }
        
        output.appendChild(ul);
    }
}

La figura 2 ilustra la secuencia de eventos mientras que la figura 3 muestra este ejemplo en acción; el código completo se incluye en esta descarga de los artículos.

Figura 2: El cliente envía el CategoryID seleccionado como único elemento de una matriz y el servidor devuelve una matriz de nombres de producto asociados.

Figura 3: Los productos se muestran en una lista con viñetas dentro de la categoría seleccionada.

Conclusión

JSON es un formato de intercambio de datos ligero, basado en texto y en un subconjunto de notación literal del lenguaje de programación JavaScript. Ofrece una codificación concisa para estructuras de datos de aplicación y se usa típicamente en escenarios donde se dispone de una implementación de JavaScript en las dos aplicaciones que intercambian datos o en una de ellas, como por ejemplo en aplicaciones web de estilo Ajax. La ventaja de JSON reside en que es sencillo de entender, adoptar, e implementar. Prácticamente JSON no tiene proceso de aprendizaje para los desarrolladores ya familiarizados con JavaScript u otros lenguajes de programación con compatibilidad semejante para una notación literal enriquecida (como Python y Ruby). El análisis de texto JSON en código JavaScript se puede lograr fácilmente mediante una llamada a la función eval, y la creación de texto JSON es muy sencilla con la secuencia de comandos json.js que se ofrece en http://www.json.org/json.js.

Existe un número cada vez mayor de bibliotecas para trabajar con JSON en las plataformas e infraestructuras principales. En este artículo analizamos Jayrock, una biblioteca de código abierto para la creación y el análisis de texto JSON en aplicaciones .NET. Jayrock se puede usar en aplicaciones ASP.NET 1.x, 2.0 y Mono. AJAX de ASP.NET ofrece una funcionalidad semejante a JSON, pero sólo para aplicaciones ASP.NET 2.0.

Suerte con la programación.

¿Ajax o AJAX?

Jesse James Garrett acuñó inicialmente el término Ajax para describir el estilo de aplicaciones web y el conjunto de tecnologías implicadas en la creación de aplicaciones web altamente interactivas. Históricamente, el término Ajax se extendió alrededor de la Web como la sigla AJAX, que significa Asynchronous JavaScript And XML. Sin embargo, con el tiempo, se consideró que la "X" de AJAX no era muy representativa del formato de datos subyacentes usados para comunicarse con el servidor web en segundo plano porque la mayoría de las implementaciones cambiaron a JSON como alternativa más sencilla y eficiente. Así que en lugar de proponer una sigla de reemplazo como AJAJ que se asemeja a un trabalenguas, la sigla generalmente se retira en favor de Ajax como término en lugar de AJAX como sigla.

En el momento en que se escribe este artículo, no se extrañe si ve que se usa de forma mezclada y amplia "AJAX" y "Ajax" para significar lo mismo. En este artículo, nos hemos ceñido a usar Ajax como término. Sin embargo, los productos comerciales que ofrecen marcos que permiten el uso de aplicaciones de estilo Ajax, tienden a usar la forma de la sigla para distinguirse del producto de un agente de limpieza con nombre similar y evitar potenciales disputas en torno a marcas registradas o asuntos jurídicos.

Referencias

Agradecimientos especiales

Antes de enviar este artículo a MSDN, varios voluntarios ayudaron a corregir el artículo y ofrecieron comentarios sobre el contenido, la gramática y la dirección que tomaba. Los principales colaboradores en el proceso de revisión son, entre otros, Douglas Crockford, Eric Schönholzer y Milán Negovan.

Acerca de los autores

Atif Aziz es consultor principal en Skybow AG, donde su trabajo principal consiste en ayudar a los clientes a entender y crear soluciones en la plataforma de desarrollo .NET. Atif colabora periódicamente con la comunidad de desarrolladores de Microsoft mediante conferencias y artículos en publicaciones técnicas. Es conferenciante de INETA y presidente del mayor grupo de usuarios .NET suizo. Puede ponerse en contacto con él en atif.aziz@skybow.com o a través de su sitio web en http://www.raboof.com/.

Scott Mitchell, autor de seis libros sobre ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha trabajado con las tecnologías web de Microsoft desde 1998. Scott trabaja como consultor, formador y escritor independiente. Puede ponerse en contacto con él en mitchell@4guysfromrolla.com o mediante su blog: http://scottonwriting.net/.

Mostrar:
© 2015 Microsoft