Uma introdução à JSON (JavaScript Object Notation) em JavaScript e .NET

 

Uma introdução à JSON (JavaScript Object Notation) em JavaScript e .NET

Atif Aziz, Scott Mitchell

Fevereiro de 2007

Aplica-se a:
   JSON
   Ajax

Resumo: Este artigo discute a Notação de Objeto JavaScript (ou JSON), um formato de troca de dados aberto e baseado em texto, que fornece um formato de troca de dados padronizado mais adequado para aplicativos Web no estilo Ajax. (22 páginas impressas)

Sumário

Introdução
Noções básicas sobre notação literal em JavaScript
Comparando JSON com XML
Criando e analisando mensagens JSON com JavaScript
Trabalhando com JSON no .NET Framework
Conclusão
Referências

Baixe o código-fonte deste artigo.

Introdução

Ao criar um aplicativo que se comunicará com um computador remoto, um formato de dados e um protocolo de troca devem ser selecionados. Há uma variedade de opções abertas e padronizadas e a opção ideal depende dos requisitos de aplicativos e da funcionalidade pré-existente. Por exemplo, os serviços Web baseados em SOAP formatam os dados em uma carga XML encapsulada em um envelope SOAP.

Embora o XML funcione bem para muitos cenários de aplicativo, ele tem algumas desvantagens que o tornam menos do que ideal para outros. Um espaço em que o XML geralmente é menor que o ideal é com aplicativos Web no estilo Ajax. O Ajax é uma técnica usada para criar aplicativos Web interativos que fornecem uma experiência de usuário mais rápido por meio do uso de chamadas leves e fora de banda para o servidor Web em vez de postbacks de página inteira. Essas chamadas assíncronas são iniciadas no cliente usando JavaScript e envolvem a formatação de dados, o envio para um servidor Web e a análise e o trabalho com os dados retornados. Embora a maioria dos navegadores possa construir, enviar e analisar XML, a JavaScript Object Notation (ou JSON) fornece um formato de troca de dados padronizado mais adequado para aplicativos Web no estilo Ajax.

JSON é um formato aberto de troca de dados baseado em texto (consulte RFC 4627). Assim como o XML, ele é legível por humanos, independente de plataforma e desfruta de uma ampla disponibilidade de implementações. Os dados formatados de acordo com o padrão JSON são leves e podem ser analisados por implementações JavaScript com facilidade incrível, tornando-o um formato de troca de dados ideal para aplicativos Web Ajax. Como ele é principalmente um formato de dados, o JSON não se limita apenas a aplicativos Web Ajax e pode ser usado em praticamente qualquer cenário em que os aplicativos precisem trocar ou armazenar informações estruturadas como texto.

Este artigo examina o padrão JSON, sua relação com JavaScript e como ele se compara ao XML. Jayrock, uma implementação JSON de software livre para .NET, é discutida e exemplos de criação e análise de mensagens JSON são fornecidos em JavaScript e C#.

Noções básicas sobre notação literal em JavaScript

Literais são usados em linguagens de programação para expressar literalmente valores fixos, como o valor inteiro constante de 4 ou a cadeia de caracteres "Olá, Mundo". Literais podem ser usados na maioria dos idiomas onde quer que uma expressão seja permitida, como parte de uma condição em uma instrução de controle, um parâmetro de entrada ao chamar uma função, em atribuição de variável e assim por diante. Por exemplo, o código C# e Visual Basic a seguir inicializa a variável x com o valor inteiro constante de 42.

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

Diferentes linguagens de programação permitem literais de diferentes tipos. A maioria das linguagens de programação dá suporte, no mínimo, a literais para tipos escalares, como inteiros, números de ponto flutuante, cadeias de caracteres e boolianos. O que é interessante no JavaScript é que, além de tipos escalares, ele também dá suporte a literais para tipos estruturados, como matrizes e objetos. Esse recurso permite uma sintaxe terse para criação sob demanda e inicialização de matrizes e objetos.

Literais de matriz em JavaScript são compostos por zero ou mais expressões, com cada expressão representando um elemento da matriz. Os elementos da matriz são colocados entre colchetes ([]) e delimitados por vírgulas. O exemplo a seguir define uma matriz literalmente com sete elementos de cadeia de caracteres que contêm os nomes dos sete continentes:

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

Compare isso agora com como você criaria e inicializaria uma matriz em JavaScript sem a notação 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";
  

Um literal de objeto define os membros de um objeto e seus valores. A lista de membros e valores de objeto está entre chaves ({}) e cada membro é delimitado por uma vírgula. Dentro de cada membro, o nome e o valor são delimitados por dois-pontos (:). O exemplo a seguir cria um objeto e o inicializa com três membros chamados Address, City e PostalCode com os respectivos valores "123 Anywhere St.", "Springfield" e "99999".

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

Os exemplos apresentados até agora ilustram o uso de literais numéricos e de cadeia de caracteres dentro de literais de matriz e objeto. Você também pode expressar um grafo inteiro usando a notação recursivamente para que os elementos de matriz e os valores de membro do objeto possam, por sua vez, usar literais de objeto e matriz. Por exemplo, o snippet a seguir ilustra um objeto que tem uma matriz como membro (PhoneNumbers), em que a matriz é composta por uma 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 Uma discussão mais completa sobre o suporte literal para JavaScript pode ser encontrada no Guia Principal do JavaScript 1.5 na seção Literais.

De Literais JavaScript a JSON

JSON é um formato de troca de dados que foi criado a partir de um subconjunto da notação de objeto literal em JavaScript. Embora a sintaxe aceita pelo JavaScript para valores literais seja muito flexível, é importante observar que o JSON tem regras muito mais rígidas. De acordo com o padrão JSON, por exemplo, o nome de um membro do objeto deve ser uma cadeia de caracteres JSON válida. Uma cadeia de caracteres em JSON deve estar entre aspas. O JavaScript, por outro lado, permite que os nomes dos membros do objeto sejam delimitados por aspas ou apóstrofos ou omitam aspas completamente, desde que o nome do membro não entre em conflito com uma palavra-chave JavaScript reservada. Da mesma forma, um elemento de matriz ou um valor de membro de objeto no JSON é limitado a um conjunto muito limitado. No JavaScript, no entanto, elementos de matriz e valores de membro do objeto podem se referir a praticamente qualquer expressão JavaScript válida, incluindo chamadas de função e definições!

O charme do JSON está em sua simplicidade. Uma mensagem formatada de acordo com o padrão JSON é composta por um único objeto ou matriz de nível superior. Os elementos da matriz e os valores de objeto podem ser objetos, matrizes, cadeias de caracteres, números, valores boolianos (verdadeiros e falsos) ou nulos. Isso, em poucas palavras, é o padrão JSON! É muito simples. Consulte www.json.org ou RFC 4627 para obter uma descrição mais formal do padrão.

Um dos pontos doloridos do JSON é a falta de um literal de data/hora. Muitas pessoas ficam surpresas e desapontadas ao saber disso quando encontram JSON pela primeira vez. A explicação simples (consolando ou não) para a ausência de um literal de data/hora é que JavaScript nunca teve um: o suporte para valores de data e hora em JavaScript é totalmente fornecido por meio do objeto Date . A maioria dos aplicativos que usam JSON como um formato de dados, portanto, geralmente tendem a usar uma cadeia de caracteres ou um número para expressar valores de data e hora. Se uma cadeia de caracteres for usada, você geralmente poderá esperar que ela esteja no formato ISO 8601. Se um número for usado, em vez disso, o valor geralmente será levado para significar o número de milissegundos em UTC (Tempo Coordenado Universal) desde a época, onde a época é definida como meia-noite de 1º de janeiro de 1970 (UTC). Novamente, esta é uma mera convenção e não faz parte do padrão JSON. Se você estiver trocando dados com outro aplicativo, precisará verificar sua documentação para ver como ele codifica valores de data e hora em um literal JSON. Por exemplo, o ASP.NET AJAX da Microsoft não usa nenhuma das convenções descritas. Em vez disso, ele codifica valores do .NET DateTime como uma cadeia de caracteres JSON, em que o conteúdo da cadeia de caracteres é \/Date(ticks)\/ e onde os tiques representam milissegundos desde a época (UTC). Portanto, 29 de novembro de 1989, 4:55:30, em UTC é codificado como "\/Date(628318530718)\/". Para obter alguma lógica por trás dessa escolha bastante inventada de codificação, consulte "Inside ASP.NET AJAX's JSON date and time string.".

Comparando JSON com XML

O JSON e o XML podem ser usados para representar objetos nativos na memória em um formato de troca de dados baseado em texto, legível por humanos. Além disso, os dois formatos de troca de dados são isomórficos , dado texto em um formato, um equivalente é concebível no outro. Por exemplo, ao chamar um dos serviços Web acessíveis publicamente do Yahoo!', você pode indicar por meio de um parâmetro querystring se a resposta deve ser formatada como XML ou JSON. Portanto, ao decidir sobre um formato de troca de dados, não é uma questão simples de escolher um em vez do outro como marcador de prata, mas sim qual formato tem as características que o tornam a melhor opção para um aplicativo específico. Por exemplo, XML tem suas raízes no texto do documento de marcação e tende a brilhar muito bem nesse espaço (como é evidente com XHTML). O JSON, por outro lado, tem suas raízes em tipos e estruturas de linguagem de programação e, portanto, fornece um mapeamento mais natural e prontamente disponível para trocar dados estruturados. Além desses dois pontos iniciais, a tabela a seguir ajudará você a entender e comparar as principais características de XML e JSON.

Principais diferenças características entre XML e JSON

Característica XML JSON
Tipos de dados Não fornece nenhuma noção de tipos de dados. É necessário contar com o Esquema XML para adicionar informações de tipo. Fornece tipos de dados escalares e a capacidade de expressar dados estruturados por meio de matrizes e objetos.
Suporte para matrizes As matrizes precisam ser expressas por convenções, por exemplo, por meio do uso de um elemento espaço reservado externo que modela o conteúdo das matrizes como elementos internos. Normalmente, o elemento externo usa a forma plural do nome usado para elementos internos. Suporte à matriz nativa.
Suporte para objetos Os objetos precisam ser expressos por convenções, muitas vezes por meio de um uso misto de atributos e elementos. Suporte a objeto nativo.
Suporte nulo Requer o uso de xsi:nil em elementos em um documento de instância XML, além de uma importação do namespace correspondente. Reconhece nativamente o valor nulo .
Comentários Suporte nativo e geralmente disponível por meio de APIs. Sem suporte.
Namespaces Dá suporte a namespaces, o que elimina o risco de colisões de nome ao combinar documentos. Os namespaces também permitem que os padrões existentes baseados em XML sejam estendidos com segurança. Nenhum conceito de namespaces. A nomenclatura de colisões geralmente é evitada por aninhamento de objetos ou pelo uso de um prefixo em um nome de membro do objeto (o primeiro é preferencial na prática).
Formatação de decisões Complexo. Requer um esforço maior para decidir como mapear tipos de aplicativo para elementos XML e atributos. Pode criar debates aquecidos se uma abordagem centrada em elementos ou centrada em atributo é melhor. Simples. Fornece um mapeamento muito mais direto para os dados do aplicativo. A única exceção pode ser a ausência de literal de data/hora.
Tamanho Os documentos tendem a ser longos em tamanho, especialmente quando uma abordagem centrada em elementos para formatação é usada. A sintaxe é muito terse e gera texto formatado em que a maior parte do espaço é consumida (com razão) pelos dados representados.
Análise em JavaScript Requer uma implementação do DOM XML e um código de aplicativo adicional para mapear o texto de volta para objetos JavaScript. Nenhum código de aplicativo adicional necessário para analisar texto; pode usar a função de avaliação do JavaScript.
Curva de aprendizado Geralmente, tende a exigir o uso de várias tecnologias em conjunto: XPath, Esquema XML, XSLT, Namespaces XML, DOM e assim por diante. Pilha de tecnologia muito simples que já é familiar para desenvolvedores com uma tela de fundo em JavaScript ou outras linguagens de programação dinâmica.

O JSON é um formato de troca de dados relativamente novo e não tem os anos de adoção ou suporte de fornecedor que o XML desfruta hoje (embora o JSON esteja se atualizando rapidamente). A tabela a seguir destaca o estado atual das coisas nos espaços XML e JSON.

Diferenças de suporte entre XML e JSON

Suporte XML JSON
Ferramentas Desfruta de um conjunto maduro de ferramentas amplamente disponíveis de muitos fornecedores do setor. O suporte a ferramentas avançadas, como editores e formatores, é escasso.
Microsoft .NET Framework Suporte muito bom e maduro desde a versão 1.0 do .NET Framework. O suporte XML está disponível como parte da BCL (Biblioteca de Classes Base). Para ambientes não gerenciados, há MSXML. Nenhum até agora, exceto uma implementação inicial como parte de ASP.NET AJAX.
Plataforma e idioma Analisadores e formatadores estão amplamente disponíveis em várias plataformas e idiomas (implementações comerciais e código aberto). Analisadores e formatadores já estão disponíveis em muitas plataformas e em muitos idiomas. Consulte json.org para obter um bom conjunto de referências. A maioria das implementações, por enquanto, tende a ser código aberto projetos.
Idioma integrado Os fornecedores do setor estão atualmente experimentando suporte literalmente dentro de idiomas. Consulte o projeto LINQ da Microsoft para obter mais informações. Há suporte nativo apenas em JavaScript/ECMAScript.

Nota Nenhuma das tabelas se destina a ser uma lista abrangente de pontos de comparação. Há outros ângulos nos quais ambos os formatos de dados podem ser comparados, mas achamos que esses pontos-chave devem ser suficientes para criar uma impressão inicial.

Criando e analisando mensagens JSON com JavaScript

Ao usar o JSON como formato de troca de dados, duas tarefas comuns estão transformando uma representação nativa e na memória em sua representação de texto JSON e vice-versa. Infelizmente, no momento da gravação, o JavaScript não fornece funções internas para criar texto JSON de um determinado objeto ou matriz. Espera-se que esses métodos sejam incluídos na quarta edição do padrão ECMAScript em 2007. Até que essas funções de formatação JSON sejam formalmente adicionadas ao JavaScript e amplamente disponíveis em implementações populares, use o script de implementação de referência disponível para download em http://www.json.org/json.js.

Em sua iteração mais recente no momento desta gravação, o script json.js em www.json.org adiciona funções toJSONString() à matriz, cadeia de caracteres, booliano, objeto e outros tipos JavaScript. As funções toJSONString() para tipos escalares (como Número e Booleano) são bastante simples, pois elas só precisam retornar uma representação de cadeia de caracteres do valor da instância. A função toJSONString() para o tipo booliano , por exemplo, retorna a cadeia de caracteres "true" se o valor for verdadeiro e "false" caso contrário. As funções toJSONString() para tipos Array e Object são mais interessantes. Para instâncias de Matriz, a função toJSONString() para cada elemento contido é chamada em sequência, com os resultados sendo concatenados com vírgulas para delimitar cada resultado. A saída final entre colchetes. Da mesma forma, para instâncias de objeto, cada membro é enumerado e sua função toJSONString() invocada. O nome do membro e a representação JSON de seu valor são concatenados com dois-pontos no meio; cada par de nomes e valores de membro é delimitado com uma vírgula e toda a saída é entre colchetes.

O resultado líquido das funções toJSONString() é que qualquer tipo pode ser convertido em seu formato JSON com uma única chamada de função. O JavaScript a seguir cria um objeto Array e adiciona sete elementos String deliberadamente usando o método detalhado e não literal para fins ilustrativos. Em seguida, ele passa a exibir a representação JSON das matrizes:

  
    // 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());
  

Bb299886.intro_to_json01(en-us,MSDN.10).gif

Figura 1. A função toJSONString() emite a matriz formatada de acordo com o padrão JSON.

Analisar texto JSON é ainda mais simples. Como json é apenas um subconjunto de literais JavaScript, ele pode ser analisado em uma representação na memória usando a função , eval(expr)tratando o texto JSON de origem como código-fonte JavaScript. A função eval aceita como entrada uma cadeia de caracteres de código JavaScript válido e avalia a expressão. Consequentemente, a seguinte linha única de código é tudo o que é necessário para transformar o texto JSON em uma representação nativa:

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

Nota Os parênteses extras são usados para tornar a avaliação incondicionalmente tratar a entrada de origem como uma expressão. Isso é especialmente importante para objetos. Se você tentar chamar a avaliação com uma cadeia de caracteres contendo texto JSON que define um objeto, como a cadeia de caracteres "{}" (que significa um objeto vazio), ela simplesmente retornará indefinida como o resultado analisado. Os parênteses forçam o analisador JavaScript a ver as chaves de nível superior como a notação literal de uma instância de objeto, em vez de, digamos, chaves que definem um bloco de instrução. Aliás, o mesmo problema não ocorrerá se o item de nível superior for uma matriz, como em eval("[1,2,3]"). Por causa da uniformidade, no entanto, o texto JSON deve estar sempre cercado de parênteses antes de chamar eval para que não haja ambiguidade sobre como interpretar a origem.

Ao avaliar a notação literal, uma instância correspondente à sintaxe literal é retornada e atribuída ao valor. Considere o exemplo a seguir, que usa a função eval para analisar a notação literal de uma matriz e atribuir a matriz resultante aos continentes variáveis.

  
    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.");
  

É claro que, na prática, o texto JSON avaliado virá de alguma fonte externa em vez de ser codificado como no caso acima.

A função de avaliação avalia cegamente qualquer expressão passada. Uma fonte não confiável poderia, portanto, incluir JavaScript potencialmente perigoso junto com ou misturado à notação literal que compõe os dados JSON. Em cenários em que a origem não pode ser confiável, é altamente recomendável analisar o texto JSON usando a função parseJSON() (encontrada em json.js):

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

A função parseJSON() também usa eval, mas somente se a cadeia de caracteres contida em arrayAsJSONText estiver em conformidade com o padrão de texto JSON. Ele faz isso usando um teste de expressão regular inteligente.

Trabalhando com JSON no .NET Framework

O texto JSON pode ser facilmente criado e analisado a partir do código JavaScript, que faz parte do seu allure. No entanto, quando o JSON é usado em um aplicativo Web ASP.NET, apenas o navegador desfruta do suporte do JavaScript, pois o código do lado do servidor provavelmente é escrito em Visual Basic ou C#.

A maioria das bibliotecas do Ajax projetadas para ASP.NET oferecem suporte para criar e analisar texto JSON programaticamente. Portanto, para trabalhar com JSON em um aplicativo .NET, considere usar uma dessas bibliotecas. Há muitas opções de software livre e de terceiros, e a Microsoft também tem sua própria biblioteca Ajax chamada ASP.NET AJAX.

Neste artigo, examinaremos exemplos que usam o Jayrock, uma implementação de software livre do JSON para o Microsoft .NET Framework criado pelo coautor Atif Aziz. Optamos por usar Jayrock em vez de ASP.NET AJAX por três razões:

  • O Jayrock é de software livre, tornando possível estender ou personalizar conforme necessário.
  • O Jayrock pode ser usado em aplicativos 1.x, 2.0 e Mono ASP.NET, enquanto ASP.NET AJAX é apenas para ASP.NET versão 2.0.
  • O escopo de Jayrock é limitado a JSON e JSON-RPC, e o primeiro é o foco principal deste artigo. Embora ASP.NET AJAX inclua algum suporte para criar e analisar texto JSON, sua principal finalidade é oferecer uma plataforma avançada para a criação de aplicativos Web de ponta a ponta no estilo Ajax em ASP.NET. Os sinos e assobios extras podem estar distraindo quando seu foco principal é JSON.

Trabalhar com JSON no .NET usando o Jayrock é semelhante ao trabalho com XML por meio das classes XmlWriter, XmlReader e XmlSerializer no .NET Framework. As classes JsonWriter, JsonReader, JsonTextWriter e JsonTextReader encontradas em Jayrock imitam a semântica das classes .NET Framework XmlWriter, XmlReader, XmlTextWriter e XmlTextReader. Essas classes são úteis para interfiguração com JSON em um nível baixo e orientado a fluxo. Usando essas classes, o texto JSON pode ser criado ou analisado por meio de uma série de chamadas de método. Por exemplo, o uso do método de classe JsonWriterWriteNumber(number) grava a representação de cadeia de caracteres apropriada de acordo com o padrão JSON. A classe JsonConvert oferece métodos de Exportação e Importação para conversão entre tipos .NET e JSON. Esses métodos fornecem uma funcionalidade semelhante, conforme encontrado nos métodos de classe XmlSerializerSerialize e Desserializar, respectivamente.

Criando texto JSON

O código a seguir ilustra o uso da classe JsonTextWriter para criar o texto JSON para uma matriz de cadeia de caracteres de continentes. Esse texto JSON é enviado para uma instância do TextWriter passada para o construtor, que por acaso é o fluxo de saída do console neste exemplo (em ASP.NET você pode usar Response.Output em vez disso):

  
    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();
}
  

Além dos métodos WriteStartArray, WriteString e WriteEndArray , a classe JsonWriter fornece métodos para escrever outros tipos de valor JSON, como WriteNumber, WriteBoolean, WriteNull e assim por diante. Os métodos WriteStartObject, WriteEndObject e WriteMember criam o texto JSON para um objeto. O exemplo a seguir ilustra a criação do texto JSON para o objeto de contato examinado na seção "Noções básicas sobre notação literal em JavaScript":

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

private static void WritePhoneNumber(JsonWriter w, string location,
    string number)
{
    w.WriteStartObject();      //  {
    w.WriteMember("Location"); //      "Location" : 
    w.WriteString(location);   //          "...", 
    w.WriteMember("Number");   //      "Number" :
    w.WriteString(number);     //          "..."
    w.WriteEndObject();        //  }
}

Os métodos Export e ExportToString na classe JsonConvert podem ser usados para serializar um tipo .NET especificado em texto JSON. Por exemplo, em vez de compilar manualmente o texto JSON para a matriz dos sete continentes usando a classe JsonTextWriter , a seguinte chamada para JsonConvert.ExportToString produz os mesmos resultados:

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

Analisando texto JSON

A classe JsonTextReader fornece uma variedade de métodos para analisar os tokens de texto JSON com o principal sendo Leitura. Cada vez que o método Read é invocado, o analisador consome o próximo token, que pode ser um valor de cadeia de caracteres, um valor numérico, um nome de membro do objeto, o início de uma matriz e assim por diante. Quando aplicável, o texto analisado do token atual pode ser acessado por meio da propriedade Text . Por exemplo, se o leitor estiver sentado em dados boolianos, a propriedade Text retornará "true" ou "false" dependendo do valor real da análise.

O código de exemplo a seguir usa a classe JsonTextReader para analisar a representação de texto JSON de uma matriz de cadeia de caracteres que contém os nomes dos sete continentes. Cada continente que começa com a letra "A" é enviado para o console:

  
    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 A classe JsonTextReader em Jayrock é um analisador de texto JSON bastante liberal. Na verdade, ele permite muito mais sintaxe do que é considerado texto JSON válido de acordo com as regras estabelecidas no RFC 4627. Por exemplo, a classe JsonTextReader permite que comentários de linha única e de várias linhas apareçam no texto JSON, como você esperaria em JavaScript. Os comentários de linha única começam com barras (//) e comentários de várias linhas sendo com slash-star (/*) e terminam em star-slash (*/). Os comentários de linha única podem até começar com o sinal de hash/libra (#), que é comum entre arquivos de configuração no estilo Unix. Em todas as instâncias, os comentários são completamente ignorados pelo analisador e nunca expostos por meio da API. Também como em JavaScript, JsonTextReader permite que uma cadeia de caracteres JSON seja delimitada por um apóstrofo ('). O analisador pode até mesmo tolerar uma vírgula extra após o último membro de um objeto ou elemento de uma matriz.

Mesmo com todas essas adições, JsonTextReader é um analisador de conformidade! JsonTextWriter, por outro lado, produz apenas texto JSON estrito de conformidade padrão. Isso segue o que muitas vezes é cunhado como a entidade de segurança de robustez, que afirma: "Seja conservador no que você faz; ser liberal no que você aceita dos outros.

Para converter o texto JSON diretamente em um objeto .NET, use o método de importação de classe JsonConvert , especificando o tipo de saída e o texto JSON. O exemplo a seguir mostra a conversão de uma matriz JSON de cadeias de caracteres em uma matriz de cadeias de caracteres .NET:

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

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

Aqui está um exemplo mais interessante de conversão que usa um feed XML RSS, desserializa-o em um tipo .NET usando XmlSerializer e converte o objeto em texto JSON usando JsonConvert (convertendo efetivamente RSS em texto XML em JSON):

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

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

using (XmlReader reader = XmlReader.Create("https://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 A definição de RichSiteSummary e seus tipos relacionados pode ser encontrada nos exemplos que acompanham este artigo.

Usando JSON no ASP.NET

Tendo olhado para maneiras de trabalhar com JSON em JavaScript e de dentro do .NET Framework usando Jayrock, é hora de recorrer a um exemplo prático de onde e como todo esse conhecimento pode ser aplicado. Considere o recurso de retorno de chamada de script do cliente no ASP.NET 2.0, que simplifica o processo de fazer chamadas fora de banda do navegador da Web para a página ASP.NET (ou para um controle específico na página). Durante um cenário típico de retorno de chamada, o script do lado do cliente nos pacotes do navegador e envia dados de volta para o servidor Web para algum processamento por um método do lado do servidor. Depois de receber os dados de resposta do servidor, o cliente usa-os para atualizar a exibição do navegador.

Nota Mais informações podem ser encontradas no artigo script callbacks da revista MSDN no ASP.NET 2.0.

O desafio em um cenário de retorno de chamada do cliente é que o cliente e o servidor só podem enviar uma cadeia de caracteres para frente e para trás. Portanto, as informações a serem trocadas devem ser convertidas de uma representação nativa na memória em uma cadeia de caracteres antes de serem enviadas e, em seguida, analisadas de uma cadeia de caracteres de volta para sua representação nativa na memória quando recebida. O recurso de retorno de chamada de script do cliente no ASP.NET 2.0 não requer um formato de cadeia de caracteres específico para os dados trocados, nem fornece nenhuma funcionalidade interna para converter entre as representações nativas na memória e na cadeia de caracteres; cabe ao desenvolvedor implementar a lógica de conversão com base em algum formato de troca de dados de sua escolha.

O exemplo a seguir ilustra como usar o JSON como o formato de troca de dados em um cenário de retorno de chamada de script do cliente. Em particular, o exemplo consiste em uma página ASP.NET que usa dados do banco de dados Northwind para fornecer uma listagem das categorias em uma lista suspensa; os produtos na categoria selecionada são exibidos em uma lista com marcadores (consulte a Figura 3). Sempre que a lista suspensa é alterada no lado do cliente, um retorno de chamada é feito passando em uma matriz cujo único elemento é o CategoryID selecionado.

Nota Estamos passando uma matriz que contém o CategoryID selecionado como seu único elemento (em vez de apenas o CategoryID) porque o padrão JSON exige que qualquer texto JSON tenha um objeto ou uma matriz como sua raiz. É claro que o cliente não é necessário para passar o texto JSON para o servidor. Poderíamos ter feito com que esse exemplo passasse apenas o CategoryID selecionado como uma cadeia de caracteres. No entanto, queríamos demonstrar o envio de texto JSON nas mensagens de solicitação e resposta do retorno de chamada.

O código a seguir no manipulador de eventos Page_Load configura o controle Web DropDownList de Categorias para que, quando ele for alterado, a função GetProductsForCategory seja chamada e passe o valor das listas suspensas selecionadas. Essa função iniciará o retorno de chamada de script do cliente se o valor da lista suspensa passada for maior que zero:

  
    // 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);
  

O método GetCallBackEventReference na classe ClientScriptManager , que é usada para gerar o código JavaScript que invoca o retorno de chamada, tem a seguinte assinatura:

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

O parâmetro de argumento especifica quais dados são enviados do cliente para o servidor Web durante o retorno de chamada e o parâmetro clientCallback especifica o nome da função do lado do cliente a ser invocada após a conclusão do retorno de chamada (showProducts). A chamada do método GetCallBackEventReference gera o seguinte código JavaScript e o adiciona à marcação renderizada:

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

'[' + categoryID + ']' é o valor passado para o servidor durante o retorno de chamada (uma matriz com um único elemento, categoryID) e showProducts é a função JavaScript executada quando o retorno de chamada retorna.

No lado do servidor, o método executado em resposta ao retorno de chamada usa a classe JsonConvert de Jayrock para analisar o texto JSON de entrada e formatar o texto JSON de saída. Em particular, os nomes dos produtos associados à categoria selecionada são recuperados e retornados como uma matriz de cadeia de caracteres.

  
    // 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 A classe JsonConvert é usada duas vezes— uma vez para converter o texto JSON em eventArgument em uma matriz de inteiros e, em seguida, converter os productNames da matriz de cadeia de caracteres em texto JSON para retornar ao cliente. Como alternativa, poderíamos ter usado as classes JsonReader e JsonWriter aqui, mas jsonConvert faz o mesmo trabalho muito bem quando os dados envolvidos são relativamente pequenos e facilmente mapeados para tipos existentes.

Quando os dados são retornados do lado do servidor, a função JavaScript especificada do método GetCallBackEventReference é chamada e passada o valor retornado. Este método JavaScript, showProducts, começa fazendo referência ao <elemento div>ProductOutput. Em seguida, ele analisa a resposta JSON e adiciona dinamicamente uma lista não ordenada com um item de lista para cada elemento de matriz. Se nenhum produto for retornado para a categoria selecionada, uma mensagem correspondente será exibida.

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);
    }
}

A Figura 2 ilustra a sequência de eventos, enquanto a Figura 3 mostra este exemplo em ação; o código completo está incluído neste download de artigos.

Bb299886.intro_to_json02(en-us,MSDN.10).gif

Figura 2: o cliente envia o CategoryID selecionado como o único elemento em uma matriz e o servidor retorna uma matriz de nomes de produto associados.

Bb299886.intro_to_json03(en-us,MSDN.10).gif

Figura 3: os produtos são exibidos em uma lista com marcadores dentro da categoria selecionada.

Conclusão

JSON é um formato leve de troca de dados baseado em texto com base em um subconjunto da notação literal da linguagem de programação JavaScript. Ele fornece uma codificação sucinta para estruturas de dados do aplicativo e normalmente é usado em cenários em que uma implementação JavaScript está disponível para um ou ambos os aplicativos trocando dados, como em aplicativos Web no estilo Ajax. O fascínio do JSON está em sua simplicidade para entender, adotar e implementar. O JSON praticamente não tem nenhuma curva de aprendizado para desenvolvedores já familiarizados com JavaScript ou outras linguagens de programação com suporte semelhante para uma notação literal avançada (como Python e Ruby). A análise de texto JSON no código JavaScript pode ser realizada simplesmente chamando a função eval , e a criação de texto JSON é uma brisa com o script json.js fornecido em http://www.json.org/json.js.

Há um número florescente de bibliotecas para trabalhar com JSON em todas as principais plataformas e estruturas. Neste artigo, examinamos o Jayrock, uma biblioteca de software livre para criar e analisar texto JSON em aplicativos .NET. O Jayrock pode ser usado em aplicativos 1.x, 2.0 e Mono ASP.NET. ASP.NET AJAX oferece funcionalidade JSON semelhante, mas apenas para ASP.NET aplicativos 2.0.

Programação feliz!

Referências

Ajax ou AJAX?

O termo Ajax foi inicialmente cunhado por Jesse James Garrett para descrever o estilo de aplicativos Web e o conjunto de tecnologias envolvidas na criação de aplicativos Web altamente interativos. Historicamente, o termo Ajax se espalhou pela Web como o acrônimo AJAX, que significa JavaScript e XML assíncronos. Com o tempo, no entanto, as pessoas perceberam que o "X" no AJAX não era muito representativo do formato de dados subjacente usado para se comunicar com o servidor Web em segundo plano, uma vez que a maioria das implementações estava mudando para JSON como uma alternativa mais simples e eficiente. Então, em vez de chegar a um acrônimo substituto como AJAJ que é um pouco de língua-twister, o acrônimo está geralmente sendo desativado em favor de Ajax o termo em vez de AJAX o acrônimo.

No momento desta escrita, espere ver um uso misto e amplo de "AJAX" e "Ajax" para significar uma e a mesma coisa. Neste artigo, ficamos com "Ajax o termo". Produtos comerciais que fornecem estruturas que permitem aplicativos no estilo Ajax, no entanto, tendem a usar o formulário de acrônimo para distinguir de um produto de agente de limpeza igualmente nomeado e para evitar possíveis disputas comerciais ou legais.

ASP.NET AJAX: dentro da cadeia de caracteres de data e hora JSON

O serializador JSON do AJAX em ASP.NET codifica uma instância datetime como uma cadeia de caracteres JSON. Durante seus ciclos de pré-lançamento, ASP.NET AJAX usou o formato "@ticks@", em que os tiques representam o número de milissegundos desde 1º de janeiro de 1970 em UTC (Tempo Coordenado Universal). Uma data e hora em UTC como 29 de novembro de 1989, 4:55:30 am seria escrita como "@62831853071@.". Embora simples e simples, esse formato não pode diferenciar entre um valor de data e hora serializado e uma cadeia de caracteres que se parece com uma data serializada, mas não deve ser desserializada como uma. Consequentemente, a equipe do ASP.NET AJAX fez uma alteração para a versão final para resolver esse problema adotando o formato "\/Date(ticks)\/".

O novo formato depende de um pequeno truque para reduzir a chance de má interpretação. No JSON, um caractere de barra (/) em uma cadeia de caracteres pode ser escapado com uma barra invertida (\) mesmo que não seja estritamente necessário. Aproveitando isso, a equipe do ASP.NET AJAX modificou JavaScriptSerializer para gravar uma instância dateTime como a cadeia de caracteres "\/Date(ticks)\/" em vez disso. O escape das duas barras avançadas é superficial, mas significativo para JavaScriptSerializer. Pelas regras JSON, "\/Date(ticks)\/" é tecnicamente equivalente a "/Date(ticks)/" mas o JavaScriptSerializer desserializará o primeiro como datetime e o último como uma cadeia de caracteres. As chances de ambiguidade são, portanto, consideravelmente menores quando comparadas com o formato "@ticks@" mais simples das pré-lançamentos.

Agradecimentos Especiais

Antes de enviar este artigo ao MSDN, tivemos vários voluntários ajudando a revisar o artigo e fornecer comentários sobre o conteúdo, gramática e direção. Os principais colaboradores do processo de revisão incluem Douglas Crockford, Eric Schönholzer e Milan Negovan.

Sobre os autores

Atif Aziz é consultor principal da Skybow AG, onde seu foco principal é ajudar os clientes a entender e criar soluções na plataforma de desenvolvimento .NET. O Atif contribui regularmente para a comunidade de desenvolvedores da Microsoft falando em conferências e escrevendo artigos para publicações técnicas. Ele é palestrante do INETA e presidente do maior grupo de usuários do .NET suíço. Ele pode ser contatado em atif.aziz@skybow.com ou através de seu site em http://www.raboof.com.

Scott Mitchell, autor de seis livros ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias web da Microsoft desde 1998. Scott trabalha como consultor independente, treinador e escritor. Ele pode ser contatado mitchell@4guysfromrolla.com por meio de seu blog: http://ScottOnWriting.net.