Front End

4 jul, 2011

JSON e JSONP – Parte 1

Publicidade

Houve uma discussão
interessante no JSMentors.com sobre JSONP e como deixá-lo mais
seguro
. Foi uma boa experiência,
principalmente porque me forçou a examinar o assunto com profundidade e
a apresentar uma contraproposta própria.

Começaremos com um resumo dos
fundamentos do JSON, incluindo a API EcmaScript5JSON, e depois discutiremos a recuperação de domínio cruzado do JSON via
JSONP. Finalmente, introduzirei um framework JSONP simples e relativamente
seguro e mostrarei como usá-lo para buscar tweets na database do Twitter.

O que é JSON?

JSON (JavaScript Object
Notation) é um formato leve de intercâmbio de dados baseado na representação
literal de Objetos, Arrays, Strings, Números e Booleans do JavaScript. Uma variação do JSON é suportada pela maioria
das linguagens modernas, e agora compete com o XML como
protocolo de dados para serviços web, http e configuração de sistemas.

O JSON foi formalizado e
popularizado por volta de 2001 por Douglas
Crockford
. A
especificação é descrita no rfc4627

OK, OK, posso obter
isso na Wikipedia. Queremos exemplos.

OK – aqui estão
alguns cookies (dos bons) expressos em JSON…

01	{
02 "cookies": {
03 "oatmeal": {
04 "ingredients": [
05 "flour",
06 "sugar",
07 "oats",
08 "butter"
09 ],
10 "calories": 430,
11 "eatBy": "2010-12-05",
12 "kosher": true
13 },
14 "chocolate": {
15 "ingredients": [
16 "flour",
17 "sugar",
18 "butter",
19 "chocolate"
20 ],
21 "calories": 510,
22 "eatBy": "2010-12-03",
23 "kosher": true
24 }
25 }
26 }

…isso é equivalente à seguinte expressão xml…

01	<cookies>
02 <oatmeal>
03 <ingredients>flour</ingredients>
04 <ingredients>sugar</ingredients>
05 <ingredients>oats</ingredients>
06 <ingredients>butter</ingredients>
07 <calories>430</calories>
08 <eatBy>2010-12-05</eatBy>
09 <kosher>true</kosher>
10 </oatmeal>
11 <chocolate>
12 <ingredients>flour</ingredients>
13 <ingredients>sugar</ingredients>
14 <ingredients>butter</ingredients>
15 <ingredients>chocolate</ingredients>
16 <calories>510</calories>
17 <eatBy>2010-12-03</eatBy>
18 <kosher>true</kosher>
19 </chocolate>
20 </cookies>

Então JSON é como
JavaScript?

Não exatamente. Embora o JSON
se pareça com o JavaScript, tem as seguintes regras restritivas:

  • O JSON
    representa seis tipos de valores: objetos, arrays, números, strings,
    booleans e o null literal.
  • Datas não são
    reconhecidas como um tipo de valor único
  • O conceito de um
    identificador JavaScript não é compreendido pelo JSON. Todos os nomes-chave têm que ser strings JSON
  • Strings JSON têm
    que estar entre aspas
  • Números JSON não
    podem ter zeros à esquerda

Além do mais, como o JSON tem
a intenção de ser uma linguagem independente, os objetos JSON devem ser
considerados como strings genéricos, e não objetos JavaScript.

Usando JSON no
JavaScript

JSON é um formato
útil para receber respostas do servidor para solicitações XHR. Aparentemente,
essa resposta será na forma de uma string. Uma forma de converter uma string
JSON em um objeto JavaScript é alimentá-la com um argumento para a função eval:

1	var myCookies = eval('(' + cookieJSON + ')');
2 myCookies.cookies.chocolate.ingredients[1]; //"sugar"

(Os parênteses extras são
necessários por causa da ambiguidade com que o JavaScript interpreta uma chave)

Transações XHR comuns estão
sujeitas às mesmas restrições de domínio, de forma que você pode ficar razoavelmente
seguro de que a resposta vem do seu próprio servidor. Entretanto, os paranóicos
entre nós vão se preocupar com as consequências de um erro do servidor, ou um
redirecionamento malicioso, e realmente um eval cego de um gremlin qualquer
enviado por seu servidor pode colocar você em dificuldades um dia.

Felizmente, o ES5 (pdf) está tomando conta de você.

JSON.parse e
JSON.stringify

O ES5 especifica um novo
objeto incorporado chamado JSON com duas funções úteis baseadas em uma API
originalmente desenvolvida por Douglas Crockford.

O JSON.parse realiza um eval seguro de supostas strings
JSON (presumivelmente através de uma expressão regular). Se a string não for um
JSON válido, uma exceção SintaxError é disparada e o eval não é chamado. Há um
segundo argumento opcional, reviver,
uma função com dois parâmetros (key e value). Se fornecida, a função reviver é aplicada a todo par key/value
produzido pelo parse, o que pode fazer com que certos valores sejam modificados
de acordo com a lógica da função. Um uso típico do reviver é na reconstituição de valores de datas de strings (embora
não tenha valor o ES5 também especificar uma função Date.prototype.toJSON).

01	function dateReviver(key, value) {
02 if (typeof value === 'string') {
03 var a = /^(d{4})-(d{2})-(d{2})$/.exec(value);
04 if (a) {
05 return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3]));
06 }
07 }
08 return value;
09 };
10
11 var myCookies = JSON.parse(cookieJSON, dateReviver);
12 myCookies.cookies.oatmeal.eatBy; //Sat Dec 04 2010 16:00:00 GMT-0800 (Pacific Standard Time)

O JSON.stringify faz o oposto. O argumento value é requerido e pode ser qualquer objeto
JavaScript (embora tipicamente seja um objeto em um array). O resultado da
invocação do stringify é uma string JSON. Também há dois argumentos opcionais,
replacer e space. Quando replacer é uma função, funciona basicamente como um inverso do reviver; contudo pode ser
também um array, quando funciona como uma lista em branco de propriedades do
objeto a ser serializado.

O argumento space é usado para formatação e seu valor pode ser tanto um número quanto uma string. Se um número é fornecido, ele representa o número de espaços em branco
com os quais identar cada nível. Se o argumento é uma string (por
exemplo ‘t’),  então o texto é identado utilizando os caracteres
contidos na string.

01	JSON.stringify(cookies, ['cookies','oatmeal','chocolate','calories'], 't')
02 /*
03 '{
04 "cookies":{
05 "oatmeal":{
06 "calories":430
07 },
08 "chocolate":{
09 "calories":510
10 }
11 }
12 }'
13 */

Ambas funções são implementadas em todos browsers modernos (mas não no
IE7). Asen Bozhilov compilou uma tabela de compatibilidade
que mostra as diferenças com que os fornecedores interpretam o JSON.parse.

?

Texto original disponível em http://javascriptweblog.wordpress.com/2010/11/29/json-and-jsonp/