Uma introdução à JSON (JavaScript Object Notation) em JavaScript e .NET
Uma introdução à JSON (JavaScript Object Notation) em JavaScript e .NET
Atif Aziz
Fevereiro de 2007
Aplica-se a:
JSON
Ajax
Resumo: Este artigo discute a JavaScript Object Notation (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 snappier 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 uma facilidade incrível, tornando-os 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 de 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 literalmente expressar 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
Linguagens de programação diferentes permitem literais de tipos diferentes. 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 booliano. O interessante sobre o 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 é colocada 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, de modo que os elementos da 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 ser colocada entre aspas. O JavaScript, por outro lado, permite que os nomes de 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 um JavaScript reservado palavra-chave. Da mesma forma, um elemento de matriz ou um valor de membro de objeto em JSON é limitado a um conjunto muito limitado. No JavaScript, no entanto, elementos de matriz e valores de membro de 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 (verdadeiro e falso) 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 (consoling 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á usado para significar o número de milissegundos em UTC (Tempo Coordenado Universal) desde a época, em que 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á marcar sua documentação para ver como ele codifica valores de data e hora em um literal JSON. Por exemplo, o AJAX ASP.NET da Microsoft não usa nenhuma das convenções descritas. Em vez disso, ele codifica valores DateTime do .NET como uma cadeia de caracteres JSON, em que o conteúdo da cadeia de caracteres é \/Date(ticks)\/ e em que 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, confira "Dentro ASP.NET cadeia de caracteres de data e hora JSON do AJAX".
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 o texto em um formato, um equivalente é concebível no outro. Por exemplo, ao chamar um dos serviços Web publicamente acessíveis 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, o 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 estruturas e tipos 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 de partida, a tabela a seguir ajudará você a entender e comparar as principais características de XML e JSON.
Principais diferenças de 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 de 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, geralmente por meio de um uso misto de atributos e elementos. | Suporte a objetos nativos. |
Suporte nulo | Requer o uso de xsi:nil em elementos em um documento de instância XML mais uma importação do namespace correspondente. | Reconhece nativamente o valor nulo . |
Comentários | Suporte nativo e geralmente disponível por meio de APIs. | Não há 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. As colisões de nomenclatura geralmente são evitadas aninhando objetos ou usando um prefixo em um nome de membro do objeto (o primeiro é preferencial na prática). |
Decisões de formatação | Complexo. Requer um esforço maior para decidir como mapear tipos de aplicativo para elementos e atributos XML. Pode criar debates acalorados se uma abordagem centrada em elemento 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 ter tamanho longo, especialmente quando uma abordagem centrada em elemento para formatação é usada. | A sintaxe é muito terse e produz 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 XML DOM 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 eval do JavaScript. |
Curva de aprendizado | Geralmente tende a exigir o uso de várias tecnologias em conjunto: XPath, Esquema XML, XSLT, Namespaces XML, o DOM e assim por diante. | Pilha de tecnologia muito simples que já é familiar para desenvolvedores com experiência 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 do 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 formatadores, é escasso. |
Microsoft .NET Framework | Suporte muito bom e maduro desde a versão 1.0 do .NET Framework. O suporte a 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 linguagens (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. |
Linguagem integrada | No momento, os fornecedores do setor estão experimentando o suporte literalmente dentro dos idiomas. Confira o projeto LINQ da Microsoft para obter mais informações. | Só há suporte nativo em JavaScript/ECMAScript. |
Nota Nenhuma das tabelas deve ser uma lista abrangente de pontos de comparação. Há outros ângulos sobre os 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 JSON como o 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 entre 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 Number e Boolean) são bastante simples, pois 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 true 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 object, 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 nome de membro e par de valores é delimitado por uma vírgula e toda a saída é colocada 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());
Figura 1. A função toJSONString() emite a matriz formatada de acordo com o padrão JSON.
A análise de 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)que trata 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 linha única de código a seguir é 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 fazer com que a avaliação trate incondicionalmente a entrada de origem como uma expressão. Isso é especialmente importante para objetos . Se você tentar chamar eval com uma cadeia de caracteres contendo texto JSON que define um objeto, como a cadeia de caracteres "{}" (ou seja, um objeto vazio), ele simplesmente retornará indefinido 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 definindo 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 uma questão de uniformidade, no entanto, o texto JSON sempre deve estar cercado por 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 embutido em código, como no caso acima.
A função eval avalia cegamente qualquer expressão passada. Uma fonte não confiável poderia, portanto, incluir JavaScript potencialmente perigoso junto com ou misturado na notação literal que compõe os dados JSON. Em cenários em que a origem não é 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 de seu fascínio. No entanto, quando o JSON é usado em um aplicativo Web ASP.NET, somente o navegador desfruta do suporte a 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 fornecem 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 de 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:
- Jayrock é de software livre, tornando possível estender ou personalizar conforme necessário.
- O Jayrock pode ser usado em aplicativos ASP.NET 1.x, 2.0 e Mono , 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 main 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 distrair quando seu foco main é JSON.
Trabalhar com JSON no .NET usando Jayrock é semelhante a trabalhar 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 a interfaçã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 número 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 à encontrada nos métodos de classe XmlSerializerSerialize e Deserialize, respectivamente.
Criando texto JSON
O código a seguir ilustra o uso da classe JsonTextWriter para criar o texto JSON para uma matriz de cadeias de caracteres de continentes. Esse texto JSON é enviado para uma instância 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 criar 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 núcleo sendo Read. 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 de análise real.
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 ao 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 barra(//) e comentários de várias linhas com barra star (/*) e terminam em barra star (*/). Os comentários de linha única podem até começar com o sinal de hash/libra (#), que é comum entre os arquivos de configuração no estilo Unix. Em todos os casos, os comentários são completamente ignorados pelo analisador e nunca expostos por meio da API. Assim como no 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 em conformidade! JsonTextWriter, por outro lado, produz apenas texto JSON estrito em conformidade com padrão. Isso segue o que muitas vezes é cunhado como a entidade 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 cadeia 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 XML em texto 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 analisado 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 de cliente no ASP.NET 2.0, o 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 no navegador empacota 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 os usa para atualizar a exibição do navegador.
Nota Mais informações podem ser encontradas no artigo da MsDN Magazine Retornos de chamada de script 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 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 de 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 conversão entre as representações nativas na memória e de 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 JSON como o formato de troca de dados em um cenário de retorno de chamada de script de 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 uma matriz cujo único elemento é o CategoryID selecionado.
Nota Estamos passando uma matriz que contém a CategoryID selecionada como seu único elemento (em vez de apenas a CategoryID), pois o padrão JSON exige que qualquer texto JSON tenha um objeto ou uma matriz como sua raiz. É claro que o cliente não precisa passar texto JSON para o servidor. Poderíamos ter feito esse exemplo passar apenas a CategoryID selecionada 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 Categories DropDownList 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 argument 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 que é passado para o servidor durante o retorno de chamada (uma matriz com um único elemento, categoryID) e showProducts é a função JavaScript que é 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. Esse método JavaScript, showProducts, começa referenciando o <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.
Figura 2: o cliente envia a CategoryID selecionada como o único elemento em uma matriz e o servidor retorna uma matriz de nomes de produtos associados.
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 de 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 criar texto JSON é uma brisa com o script json.js fornecido em http://www.json.org/json.js.
Há um número cada vez maior 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 ASP.NET aplicativos 1.x, 2.0 e Mono. ASP.NET AJAX oferece funcionalidade JSON semelhante, mas apenas para aplicativos ASP.NET 2.0.
Programação feliz!
Referências
- ASP.NET AJAX
- Chamadas de serviço Web do lado do cliente com extensões AJAX
- Guia principal do JavaScript 1.5
- Função eval(expr)
- Jayrock
- JSON.org
- Retornos de chamada de script no ASP.NET
- RFC 4627
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 Assíncrono e XML. 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, pois 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 deste escrito, 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 nomeado da mesma forma e para evitar possíveis marcas comerciais ou disputas 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 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 serializado de data e hora 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 na 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 interpretação incorreta. No JSON, um caractere de barra (/) em uma cadeia de caracteres pode ser escapado com uma barra invertida (\), embora 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 de avanço é superficial, mas significativo para JavaScriptSerializer. Pelas regras JSON,
"
\/Date(ticks)\/"
é tecnicamente equivalente a"
/Date(ticks)/"
mas o JavaScriptSerializer desserializará o primeiro como um DateTime e o último como uma String. As chances de ambiguidade são, portanto, consideravelmente menores quando comparadas ao formato mais simples de "@ticks@" das pré-lançamentos.
Agradecimentos Especiais
Antes de enviar este artigo ao MSDN, tivemos vários voluntários que ajudaram a revisar o artigo e fornecer comentários sobre o conteúdo, a gramática e a 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 do Skybow AG, onde seu foco principal é ajudar os clientes a entender e criar soluções na plataforma de desenvolvimento do .NET. O Atif contribui regularmente para a comunidade de desenvolvedores da Microsoft falando em conferências e escrevendo artigos para publicações técnicas. É palestrante da 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 em mitchell@4guysfromrolla.com ou através de seu blog: http://ScottOnWriting.net.