JSON ließ Infinity und NaN aus; JSON-Status in ECMAScript?

179

Irgendeine Idee, warum JSON NaN und +/- Infinity weggelassen hat? Es versetzt Javascript in die seltsame Situation, in der Objekte, die sonst serialisierbar wären, dies nicht tun, wenn sie NaN- oder +/- unendlich Werte enthalten.

Sieht so aus, als wäre dies in Stein gemeißelt: siehe RFC4627 und ECMA-262 (Abschnitt 24.5.2, JSON.stringify, ANMERKUNG 4, Seite 683 des PDFs ECMA-262 bei der letzten Bearbeitung):

Endliche Zahlen werden wie durch Aufrufen stringifiziert ToString(number). NaN und Infinity werden unabhängig vom Vorzeichen als String dargestellt null.

Jason S.
quelle
Ich kann dieses Zitat in keinem der Dokumente finden.
Wingedsubmariner
1
Es wurde behoben, es sieht so aus, als ob es irgendwie eine veraltete Referenz / veraltete Bearbeitung gab.
Jason S

Antworten:

90

Infinityund NaNsind keine Schlüsselwörter oder etwas Besonderes, sondern nur Eigenschaften des globalen Objekts (wie sie sind undefined) und können als solche geändert werden. Aus diesem Grund nimmt JSON sie nicht in die Spezifikation auf - im Wesentlichen sollte jede echte JSON-Zeichenfolge in EcmaScript das gleiche Ergebnis haben, wenn Sie dies tun eval(jsonString)oder JSON.parse(jsonString).

Wenn es erlaubt wäre, könnte jemand einen ähnlichen Code einfügen

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

in ein Forum (oder was auch immer) und dann könnte jede json-Nutzung auf dieser Site kompromittiert werden.

olliej
quelle
29
Wenn Sie 1/0 bewerten, erhalten Sie Unendlichkeit, wenn Sie -1/0 bewerten, erhalten Sie -Infinity, wenn Sie 0/0 bewerten, erhalten Sie NaN.
Jason S
9
Die Begriffe NaNund Infinitysind jedoch Eigenschaftsnamen. Während String (1/0) einen String erzeugt "Infinity", der nur die String-Darstellung des Werts unendlich ist. Es ist nicht möglich, entweder NaNoder Infinityals Literalwert ES darzustellen - Sie müssen entweder einen Ausdruck (z. B. 1/0, 0/0 usw.) oder eine Eigenschaftssuche (bezogen auf Infinityoder NaN) verwenden. Da diese eine Codeausführung erfordern, können sie nicht in JSON aufgenommen werden.
olliej
16
Was Sicherheit betrifft, müsste ein anständiger JSON-Parser bei der Konvertierung von NaN lediglich den Wert 0/0 liefern (anstatt das Symbol NaN auszuwerten), der unabhängig davon, was passiert, das "echte" NaN zurückgibt Das Symbol NaN wird neu definiert als.
Jason S
33
@olliej: Sie argumentieren, dass NaN kein Literal ist, ich kenne Javascript nicht genug, um die Javascript-Semantik zu beurteilen. Für ein Dateiformat, in dem Gleitkommazahlen mit doppelter Genauigkeit gespeichert werden, sollte es eine Möglichkeit geben, IEEE-Gleitkommazahlen zu definieren, dh durch ein Literal NaN / Infinity / NegInfinity. Dies sind Zustände der 64-Bit-Doubles und sollten als solche darstellbar sein. Es gibt Menschen, die (aus Gründen) von ihnen abhängig sind. Sie wurden wahrscheinlich vergessen, weil JSON / Javascript aus der Webentwicklung statt aus dem wissenschaftlichen Rechnen stammte.
Wirrbel
34
Es ist 100%, absolut FALSCH für JSON, willkürlich perfekt gültige und standardmäßige Gleitkommazahlenzustände von NaN, Infinity und -Infinity weggelassen zu haben. Im Wesentlichen entschied sich JSON, eine beliebige Teilmenge von IEEE-Float-Werten zu unterstützen, wobei drei spezifische Werte ignorant weggelassen wurden, weil sie hart oder so sind. Nein. Eval-Fähigkeit ist nicht einmal eine Entschuldigung, da solche Zahlen als Literale 1/0, -1/0 und 0/0 codiert werden könnten. Es handelt sich um gültige Zahlen, an die "/ 0" angehängt ist, was nicht nur einfach zu erkennen ist, sondern gleichzeitig auch als ES ausgewertet werden kann. Keine Entschuldigung.
Triynko
55

Zur ursprünglichen Frage: Ich stimme dem Benutzer "cbare" darin zu, dass dies eine unglückliche Auslassung in JSON ist. IEEE754 definiert diese als drei spezielle Werte einer Gleitkommazahl. Daher kann JSON IEEE754-Gleitkommazahlen nicht vollständig darstellen. Es ist sogar noch schlimmer, da JSON im Sinne von ECMA262 5.1 nicht einmal definiert, ob seine Nummern auf IEEE754 basieren. Da der in ECMA262 für die Funktion stringify () beschriebene Entwurfsablauf die drei speziellen IEEE-Werte erwähnt, kann man vermuten, dass tatsächlich die Absicht bestand, IEEE754-Gleitkommazahlen zu unterstützen.

Als ein weiterer Datenpunkt, der nichts mit der Frage zu tun hat: XML-Datentypen xs: float und xs: double geben an, dass sie auf IEEE754-Gleitkommazahlen basieren, und unterstützen die Darstellung dieser drei speziellen Werte (siehe W3C XSD 1.0 Teil 2) , Datentypen).

Andreas Maier
quelle
5
Ich bin damit einverstanden, dass dies alles unglücklich ist. Aber vielleicht ist es gut, dass JSON-Zahlen nicht das genaue Gleitkommaformat angeben. Sogar IEEE754 spezifiziert viele Formate - unterschiedliche Größen und eine Unterscheidung zwischen dezimalen und binären Exponenten. JSON eignet sich besonders gut für Dezimalzahlen, daher wäre es schade, wenn ein Standard es an Binärdaten anheften würde.
Adrian Ratnapala
5
@AdrianRatnapala +1 In der Tat: JSON-Nummern haben eine potenziell unendliche Genauigkeit und sind daher viel besser als IEEE-Spezifikationen, da sie keine Größenbeschränkung, keine Genauigkeitsbeschränkung und keinen Rundungseffekt haben (wenn der Serializer damit umgehen kann).
Arnaud Bouchez
2
@ArnaudBouchez. Trotzdem sollte JSON weiterhin Zeichenfolgen unterstützen, die NaN und + -Infinity darstellen. Selbst wenn JSON nicht an ein IEEE-Format gebunden sein sollte, sollten Personen, die das Zahlenformat definieren, zumindest die Wikipedia-Seite IEEE754 lesen und eine Weile nachdenken.
Adrian Ratnapala
Das ist nicht unglücklich. Siehe die Antwort von @CervEd. Es ist nicht an IEE754 gebunden, was eine gute Sache ist (auch wenn die meisten Programmiersprachen IEEE754 verwenden und daher im Falle von NaN usw. eine zusätzliche Verarbeitung erfordern).
Ludovic Kuty
16

Könnten Sie das Nullobjektmuster anpassen und in Ihrem JSON Werte wie darstellen?

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

Bei der Überprüfung können Sie dann nach dem Typ suchen

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

Ich weiß, dass Sie in Java Serialisierungsmethoden überschreiben können, um so etwas zu implementieren. Ich bin mir nicht sicher, woher Ihre Serialisierung stammt, daher kann ich keine Details zur Implementierung in die Serialisierungsmethoden angeben.

Zoidberg
quelle
1
hmmm ... das ist eine Antwort auf eine Problemumgehung; Ich habe nicht wirklich nach einer Problemumgehung gefragt, sondern vielmehr, warum diese Werte ausgeschlossen wurden. Aber trotzdem +1.
Jason S
2
@ Zoidberg: undefinedist kein Schlüsselwort, es ist eine Eigenschaft auf dem globalen Objekt
olliej
2
@Zoidberg: undefined ist eine Eigenschaft für das globale Objekt - es ist kein Schlüsselwort, daher wird "undefined" in thisim globalen Bereich true zurückgegeben. Es bedeutet auch, dass Sie tun können undefined = 42und if (myVar == undefined)werden (im Wesentlichen) myVar == 42. Dies geht auf die Anfänge von Ecmascript nee javascript zurück, in denen undefinedes standardmäßig nicht existierte, also taten es var undefineddie Leute nur im globalen Bereich. Folglich undefinedkonnte kein Schlüsselwort erstellt werden, ohne vorhandene Websites zu beschädigen, und so waren wir für alle Zeiten dazu verdammt, undefiniert eine normale Eigenschaft zu sein.
olliej
2
@olliej: Ich habe keine Ahnung, warum Sie denken, dass undefiniert eine Eigenschaft für das globale Objekt ist. Standardmäßig ist die Suche nach undefined der integrierte Wert von undefined. Wenn Sie es mit "undefined = 42" überschreiben, erhalten Sie beim Zugriff auf undefined als Variablensuche den überschriebenen Wert. Versuchen Sie jedoch "zz = undefined; undefined = 42; x = {}; 'undefined old =' + (xa === zz) + ', undefined new =' + (xa === undefined)". Sie können die internen Werte von null, undefined, NaN oder Infinity niemals neu definieren, selbst wenn Sie deren Symbolsuche überschreiben können.
Jason S
2
@Jason undefinedist eine globale Eigenschaft, da sie als solche angegeben ist. Konsultieren Sie 15.1.1.3 von ECMAScript-262 3rd ed.
Kangax
11

Die Zeichenfolgen "Infinity", "-Infinity" und "NaN" entsprechen alle den erwarteten Werten in JS. Ich würde also argumentieren, dass der richtige Weg, diese Werte in JSON darzustellen, in Zeichenfolgen besteht.

> +"Infinity"
Infinity

> +"-Infinity"
-Infinity

> +"NaN"
NaN

Es ist nur eine Schande, dass JSON.stringify dies nicht standardmäßig tut. Aber es gibt einen Weg:

> JSON.stringify({ x: Infinity }, function (k,v) { return v === Infinity ? "Infinity" : v; })
"{"x":"Infinity"}"
teh_senaus
quelle
1
0/0 usw. sind keine gültigen JSON. Sie müssen innerhalb der Grenzen des Standards arbeiten, und Strings machen den Job gut.
teh_senaus
Im Gegenteil, ich denke, dies ist die einzige praktische Lösung, aber ich werde eine Funktion ausführen, die NaN zurückgibt, wenn der Eingabewert "NaN" usw. ist. Die Art und Weise, wie Sie die Konvertierung durchführen, ist anfällig für Code-Injection.
Marco Sulla
3
JSON-Werte können keine arithmetischen Ausdrücke sein. Das Ziel der Trennung des Standards von der Sprachliteral-Syntax besteht darin, JSON deeserialisierbar zu machen, ohne etwas davon als Code auszuführen. Nicht sicher , warum konnten wir nicht haben NaNund Infinityhinzugefügt , wie Keyword - Werte wie trueund false, though.
Mark Reed
Um es expliziter zu machen, können wir verwenden Number("Infinity"), Number("-Infinity")undNumber("NaN")
HKTonyLee
Das ist Arbeit wie Magie. JSON.parse("{ \"value\" : -1e99999 }")leicht { value:-Infinity }in Javascript zurückkehren. Nur ist es einfach nicht kompatibel mit benutzerdefinierten Nummerntypen, die größer sein könnten
Thaina
7

Wenn Sie Zugriff auf den Serialisierungscode haben, können Sie Infinity als 1.0e + 1024 darstellen. Der Exponent ist zu groß, um in einem Doppel dargestellt zu werden, und wenn er deserialisiert ist, wird dies als Unendlichkeit dargestellt. Funktioniert mit Webkit, unsicher über andere JSON-Parser!

kuwerty
quelle
4
IEEE754 unterstützt 128-Bit-Gleitkommazahlen, daher ist 1.0e5000 besser
Ton Plomp
2
Tonne: 128 Bit wurde später hinzugefügt. Was ist, wenn sie sich entscheiden, 256 Bit hinzuzufügen? Dann müssen Sie weitere Nullen hinzufügen, und der vorhandene Code verhält sich anders. Infinitywird es immer sein Infinity, warum also nicht unterstützen?
fliegende Schafe
1
Clevere Idee! Ich wollte gerade entweder in ein anderes Format wechseln oder meinem Parser umständlichen Workaround-Code hinzufügen. Nicht für jeden Fall ideal, aber in meinem Fall, in dem Unendlichkeit nur als optimierter Randfall für eine konvergierende Sequenz dient, ist es einfach perfekt, und selbst wenn eine größere Präzision eingeführt würde, wäre es meistens richtig. Vielen Dank!
Oder Sharir
3
1, -1 und 0 ..... perfekt gültige / analysierbare Zahlen werden zu diesen drei speziellen Werten, wenn Sie sie einfach /0am Ende hinzufügen . Es ist leicht zu analysieren, sofort sichtbar und sogar auswertbar. Es ist unentschuldbar, dass sie es noch nicht zum Standard hinzugefügt haben: {"Not A Number":0/0,"Infinity":1/0,"Negative Infinity":-1/0} << Warum nicht? alert(eval("\"Not A Number\"") //works alert(eval("1/0")) //also works, prints 'Infinity'. Keine Entschuldigung.
Triynko
1

Der aktuelle IEEE Std 754-2008 enthält Definitionen für zwei verschiedene 64-Bit-Gleitkommadarstellungen: einen dezimalen 64-Bit-Gleitkommatyp und einen binären 64-Bit-Gleitkommatyp.

Nach dem Runden ist die Zeichenfolge .99999990000000006dieselbe wie .9999999in der binären 64-Bit-Darstellung von IEEE, jedoch NICHT dieselbe wie .9999999in der 64-Bit-Darstellung mit IEEE-Dezimalzahl. Bei 64-Bit-IEEE-Dezimal-Gleitkomma- .99999990000000006Runden wird auf den Wert gerundet, der .9999999000000001nicht mit der Dezimalzahl übereinstimmt.9999999 übereinstimmt.

Da JSON numerische Werte nur als numerische Zeichenfolgen mit Dezimalstellen behandelt, kann ein System, das sowohl IEEE-Binär- als auch Dezimal-Gleitkommadarstellungen (wie IBM Power) unterstützt, nicht bestimmen, welcher der beiden möglichen numerischen IEEE-Gleitkommawerte ist beabsichtigt.

Steven Hobbs
quelle
Was hat das mit der Frage zu tun? (was über Unendlichkeit und NaN ist)
Bryan
1

Mögliche Problemumgehung für Fälle wie {"Schlüssel": Unendlichkeit}:

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

Die allgemeine Idee besteht darin, das Auftreten ungültiger Werte durch eine Zeichenfolge zu ersetzen, die wir beim Parsen erkennen, und diese durch die entsprechende JavaScript-Darstellung zu ersetzen.

Shamel
quelle
Ich weiß nicht, warum diese Lösung abgelehnt wurde, denn ehrlich gesagt, wenn Sie in einer Situation sind, in der Ihre JSON-Zeichenfolge Infinity- oder IsNaN-Werte enthält, schlägt dies fehl, wenn Sie versuchen, sie zu analysieren. Mit dieser Technik ersetzen Sie zuerst Vorkommen von IsNaN oder Infinity durch etwas anderes (um sie von einer gültigen Zeichenfolge zu isolieren, die diese Begriffe enthalten könnte), und verwenden JSON.parse (Zeichenfolge, Rückruf), um die richtigen, gültigen JavaScript-Werte zurückzugeben. Ich verwende dies im Produktionscode und hatte nie ein Problem.
SHamel
Würde das nicht Infinity in Strings durcheinander bringen? In vielen Fällen ist es wahrscheinlich sicher anzunehmen, dass es sich nicht um ein Problem handelt, aber die Lösung ist nicht vollständig robust.
Olejorgenb
1

Der Grund ist auf Seite ii im Standard ECMA-404 The JSON Data Interchange Syntax, 1. Ausgabe angegeben

JSON ist Agnostiker in Bezug auf Zahlen. In jeder Programmiersprache kann es eine Vielzahl von Zahlentypen mit unterschiedlichen Kapazitäten und Ergänzungen geben, fest oder schwebend, binär oder dezimal. Dies kann den Austausch zwischen verschiedenen Programmiersprachen erschweren. JSON bietet stattdessen nur die Darstellung von Zahlen, die Menschen verwenden: eine Folge von Ziffern. Alle Programmiersprachen wissen, wie man Ziffernfolgen versteht, auch wenn sie in internen Darstellungen nicht übereinstimmen. Das reicht aus, um den Austausch zu ermöglichen.

Der Grund liegt nicht, wie viele behauptet haben, in den Darstellungen NaNund dem InfinityECMA-Skript. Einfachheit ist ein zentrales Designprinzip von JSON.

Da es so einfach ist, wird nicht erwartet, dass sich die JSON-Grammatik jemals ändern wird. Dies verleiht JSON als Grundnotation eine enorme Stabilität

CervEd
quelle
-3

Wenn Sie wie ich keine Kontrolle über den Serialisierungscode haben, können Sie mit NaN-Werten umgehen, indem Sie sie wie folgt durch null oder einen anderen Wert ersetzen:

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

Im Wesentlichen wird .fail aufgerufen, wenn der ursprüngliche JSON-Parser ein ungültiges Token erkennt. Dann wird ein String-Ersetzen verwendet, um die ungültigen Token zu ersetzen. In meinem Fall ist es eine Ausnahme für den Serialisierer, NaN-Werte zurückzugeben, daher ist diese Methode der beste Ansatz. Wenn die Ergebnisse normalerweise ein ungültiges Token enthalten, ist es besser, nicht $ .get zu verwenden, sondern das JSON-Ergebnis manuell abzurufen und immer die Zeichenfolgenersetzung auszuführen.

user1478002
quelle
21
Clever, aber nicht ganz kinderleicht. Versuchen Sie es mit{ "tune": "NaNaNaNaNaNaNaNa BATMAN", "score": NaN }
JJJ
1
und Sie müssen jQuery verwenden. Ich habe kein $ .get ().
Jason S