JSON omitiu Infinity e NaN; Status JSON no ECMAScript?

Alguma idéia do porque o JSON deixou de fora NaN e +/- Infinity? Ele coloca o Javascript na estranha situação em que objects que de outra forma seriam serializáveis ​​não são, se contiverem valores NaN ou +/- infinito.

Parece que isso foi moldado na pedra: veja RFC4627 e ECMA-262 (seção 24.3.2, JSON.stringify, NOTA 4, página 507 na última edição):

Os números finitos são esticados como se chamando ToString(number) . NaN e Infinity, independentemente do sinal, são representados como o String null .

Infinity e NaN não são palavras-chave ou nada de especial, são apenas propriedades no object global (como é undefined ) e, como tal, podem ser alteradas. É por essa razão que o JSON não os inclui na especificação – em essência, qualquer string JSON verdadeira deve ter o mesmo resultado no EcmaScript se você fizer eval(jsonString) ou JSON.parse(jsonString) .

Se fosse permitido, então alguém poderia injetar código semelhante ao

 NaN={valueOf:function(){ do evil }}; Infinity={valueOf:function(){ do evil }}; 

em um fórum (ou qualquer outro) e, em seguida, qualquer uso do json no site pode ser comprometido.

Na pergunta original: Concordo com o usuário “cbare” em que isso é uma omissão infeliz no JSON. IEEE754 define estes como três valores especiais de um número de ponto flutuante. Portanto, o JSON não pode representar totalmente os números de ponto flutuante IEEE754. Na verdade, é ainda pior, já que o JSON, conforme definido no ECMA262 5.1, não define nem se seus números são baseados em IEEE754. Como o stream de projeto descrito para a function stringify () no ECMA262 menciona os três valores especiais do IEEE, pode-se suspeitar que a intenção era, na verdade, suportar números de ponto flutuante IEEE754.

Como um outro ponto de dados, não relacionado à pergunta: os tipos de dados XML xs: float e xs: double afirmam que eles são baseados em números de ponto flutuante IEEE754 e suportam a representação desses três valores especiais (consulte W3C XSD 1.0 Parte 2). , Tipos de dados).

Você poderia adaptar o padrão de object nulo e, em seu JSON, representar valores como

 "myNum" : { "isNaN" :false, "isInfinity" :true } 

Então, ao verificar, você pode verificar o tipo

 if (typeof(myObj.myNum) == 'number') {/* do this */} else if (myObj.myNum.isNaN) {/* do that*/} else if (myObj.myNum.isInfinity) {/* Do another thing */} 

Eu sei que em Java você pode replace methods de serialização para implementar tal coisa. Não tenho certeza de onde sua serialização está, então não posso dar detalhes sobre como implementá-la nos methods de serialização.

Pode ser porque o JSON se destina a ser um formato de intercâmbio de dados que pode ser usado em uma variedade de plataformas e permitir que o NaN / Infinity o torne menos portátil.

As strings “Infinity”, “-Infinity” e “NaN” são todas coerentes com os valores esperados em JS. Então eu diria que o caminho certo para representar esses valores em JSON é como strings.

 > +"Infinity" Infinity > +"-Infinity" -Infinity > +"NaN" NaN 

É uma pena que o JSON.stringify não faça isso por padrão. Mas, existe um jeito:

 > JSON.stringify({ x: Infinity }, function (k,v) { return v === Infinity ? "Infinity" : v; }) "{"x":"Infinity"}" 

Se você tiver access ao código de serialização, poderá representar o Infinity como 1.0e + 1024. O expoente é muito grande para representar em um duplo e, quando desserializado, isso é representado como Infinito. Funciona no webkit, não tem certeza sobre outros analisadores json!

O atual IEEE Std 754-2008 inclui definições para duas representações de ponto flutuante de 64 bits diferentes: um tipo de ponto flutuante decimal de 64 bits e um tipo de ponto flutuante binário de 64 bits.

Após arredondar a cadeia, o número .99999990000000006 é igual a .9999999 na representação binária de 64 bits do IEEE, mas NÃO é igual a .9999999 na representação decimal de 64 bits do IEEE. No ponto flutuante decimal de IEEE de 64 bits, .99999990000000006 arredonda para o valor .9999999000000001 que não é o mesmo que o valor decimal .9999999 .

Como o JSON trata apenas valores numéricos como cadeias numéricas de dígitos decimais, não há como um sistema suportar representações de ponto flutuante decimal e binário IEEE (como IBM Power) para determinar quais dos dois valores de ponto flutuante IEEE possíveis são pretendido.

Solução potencial para casos como {“key”: Infinity}:

 JSON.parse(theString.replace(/":(Infinity|-InNaN)/g, '":"{{$1}}"'), function(k, v) { if (v === '{{Infinity}}') return Infinity; else if (v === '{{-Infinity}}') return -Infinity; else if (v === '{{NaN}}') return NaN; return v; }); 

A idéia geral é replace ocorrências de valores inválidos por uma string que reconheçamos ao analisar e substituí-la pela representação JavaScript apropriada.

Se como eu você não tem controle sobre o código de serialização, você pode lidar com valores NaN substituindo-os por null ou qualquer outro valor como um pouco de um hack como segue:

 $.get("file.json", theCallback) .fail(function(data) { theCallback(JSON.parse(data.responseText.replace(/NaN/g,'null'))); } ); 

Em essência, .fail será chamado quando o analisador json original detectar um token inválido. Em seguida, uma substituição de sequência é usada para replace os tokens inválidos. No meu caso, é uma exceção para o serializador retornar valores NaN, portanto, esse método é a melhor abordagem. Se os resultados normalmente contiverem token inválido, seria melhor não usar $ .get, mas recuperar manualmente o resultado JSON e sempre executar a substituição da string.

Intereting Posts