JSON ha lasciato fuori Infinity e NaN; Stato JSON in ECMAScript?

Qualche idea sul perché JSON abbia omesso NaN e +/- Infinity? Mette Javascript nella strana situazione in cui oggetti che altrimenti sarebbero serializzabili, non lo sono, se contengono valori di NaN o +/- infinito.

Sembra che questo sia stato fuso nella pietra: vedi RFC4627 e ECMA-262 (sezione 24.3.2, JSON.stringify, NOTA 4, pagina 507 all’ultima modifica):

I numeri finiti vengono definiti come se chiamassero ToString(number) . NaN e Infinity indipendentemente dal segno sono rappresentati come la stringa null .

Infinity e NaN non sono parole chiave o nulla di speciale, sono solo proprietà sull’object globale (come undefined ) e come tali possono essere modificati. È per questo motivo che JSON non li include nello specifico: in effetti, qualsiasi vera stringa JSON dovrebbe avere lo stesso risultato in EcmaScript se si esegue eval(jsonString) o JSON.parse(jsonString) .

Se fosse permesso, allora qualcuno potrebbe iniettare codice simile a

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

in un forum (o qualsiasi altra cosa) e quindi qualsiasi utilizzo di JSON su quel sito potrebbe essere compromesso.

Sulla domanda iniziale: concordo con l’utente “cbare” in quanto si tratta di una sfortunata omissione in JSON. IEEE754 definisce questi come tre valori speciali di un numero in virgola mobile. Quindi JSON non può rappresentare completamente i numeri in virgola mobile IEEE754. In realtà è anche peggio, poiché JSON come definito in ECMA262 5.1 non definisce nemmeno se i suoi numeri sono basati su IEEE754. Poiché il stream di progettazione descritto per la funzione stringify () in ECMA262 menziona i tre valori IEEE speciali, si può sospettare che l’intenzione fosse effettivamente quella di supportare i numeri in virgola mobile IEEE754.

Come un altro punto dati, non correlato alla domanda: i tipi di dati XML xs: float e xs: double do indicano che sono basati su numeri a virgola mobile IEEE754 e supportano la rappresentazione di questi tre valori speciali (vedere W3C XSD 1.0 Parte 2 , Tipi di dati).

Puoi adattare il modello di object nullo e nel tuo JSON rappresentare valori come

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

Quindi, quando si verifica, è ansible verificare il tipo

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

So che in Java è ansible sovrascrivere i metodi di serializzazione per implementare una cosa del genere. Non so da dove viene la serializzazione, quindi non posso dare dettagli su come implementarlo nei metodi di serializzazione.

Potrebbe essere perché JSON è inteso per essere un formato di interscambio di dati che può essere utilizzato in una varietà di piattaforms e consentendo a NaN / Infinity di renderlo meno portabile.

Le stringhe “Infinity”, “-Infinity” e “NaN” costringono tutti ai valori attesi in JS. Quindi direi che il modo giusto per rappresentare questi valori in JSON è come stringhe.

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

È solo un peccato che JSON.stringify non lo faccia per impostazione predefinita. Ma c’è un modo:

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

Se hai accesso al codice di serializzazione potresti rappresentare Infinity come 1.0e + 1024. L’esponente è troppo grande per rappresentare in un doppio e quando è deserializzato questo è rappresentato come Infinito. Funziona su webkit, incerto su altri parser json!

L’attuale IEEE Std 754-2008 include le definizioni per due diverse rappresentazioni a virgola mobile a 64 bit: un tipo decimale a virgola mobile a 64 bit e un tipo a virgola mobile binario a 64 bit.

Dopo aver arrotondato la stringa .99999990000000006 .9999999 a .9999999 nella rappresentazione binaria IEEE a 64 bit, ma NON è uguale a .9999999 nella rappresentazione decimale IEEE a 64 bit. In virgola mobile decimale IEEE a 64 bit .99999990000000006 arrotonda al valore .9999999000000001 che non è uguale al valore decimale .9999999 .

Poiché JSON tratta solo valori numerici come stringhe numeriche di cifre decimali, non esiste alcun modo per un sistema che supporti sia rappresentazioni in virgola mobile binarie e decimali IEEE (come IBM Power) per determinare quale dei due possibili valori numerici in virgola mobile IEEE sia previsto.

Potenziale soluzione per casi come {“chiave”: Infinito}:

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

L’idea generale è di sostituire le occorrenze di valori non validi con una stringa che riconosceremo durante l’analisi e la sostituiremo con la rappresentazione JavaScript appropriata.

Se come me non hai il controllo sul codice di serializzazione, puoi gestire i valori NaN sostituendoli con null o qualsiasi altro valore come un po ‘di hacking come segue:

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

In sostanza, .fail verrà chiamato quando il parser json originale rileva un token non valido. Quindi una stringa sostituita viene utilizzata per sostituire i token non validi. Nel mio caso è un’eccezione per il serializzatore per restituire i valori NaN, quindi questo metodo è l’approccio migliore. Se i risultati normalmente contengono token non validi, è preferibile non utilizzare $ .get ma recuperare manualmente il risultato JSON e eseguire sempre la sostituzione della stringa.