admin管理员组

文章数量:1131210

Any idea why JSON left out NaN and +/- Infinity? It puts Javascript in the strange situation where objects that would otherwise be serializable, are not, if they contain NaN or +/- infinity values.

Looks like this has been cast in stone: see RFC4627 and ECMA-262 (section 24.5.2, JSON.stringify, NOTE 4, page 683 of the ECMA-262 pdf at last edit):

Finite numbers are stringified as if by calling ToString(number). NaN and Infinity regardless of sign are represented as the String null.

Any idea why JSON left out NaN and +/- Infinity? It puts Javascript in the strange situation where objects that would otherwise be serializable, are not, if they contain NaN or +/- infinity values.

Looks like this has been cast in stone: see RFC4627 and ECMA-262 (section 24.5.2, JSON.stringify, NOTE 4, page 683 of the ECMA-262 pdf at last edit):

Finite numbers are stringified as if by calling ToString(number). NaN and Infinity regardless of sign are represented as the String null.

Share Improve this question edited Oct 7, 2021 at 5:46 CommunityBot 11 silver badge asked Sep 14, 2009 at 18:11 Jason SJason S 189k172 gold badges631 silver badges996 bronze badges 4
  • I can't find that quote in either document. – wingedsubmariner Commented Aug 24, 2015 at 17:19
  • 1 fixed it, looks like there was a stale reference / stale edit somehow. – Jason S Commented Aug 24, 2015 at 19:12
  • dbj.org/json-how-to-create-an-undefined-value-or-an-nan-value – Chef Gladiator Commented Feb 7, 2020 at 0:09
  • 1 Also in ECMA-404 2nd ed.: "Numeric values that cannot be represented as sequences of digits (such as Infinity and NaN) are not permitted" – djvg Commented Aug 25, 2023 at 17:31
Add a comment  | 

10 Answers 10

Reset to default 104

Infinity and NaN aren't keywords or anything special, they are just properties on the global object (as is undefined) and as such can be changed. It's for that reason JSON doesn't include them in the spec -- in essence any true JSON string should have the same result in EcmaScript if you do eval(jsonString) or JSON.parse(jsonString).

If it were allowed then someone could inject code akin to

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

into a forum (or whatever) and then any json usage on that site could be compromised.

On the original question: I agree with user "cbare" in that this is an unfortunate omission in JSON. IEEE754 defines these as three special values of a floating point number. So JSON cannot fully represent IEEE754 floating point numbers. It is in fact even worse, since JSON as defined in ECMA262 5.1 does not even define whether its numbers are based on IEEE754. Since the design flow described for the stringify() function in ECMA262 does mention the three special IEEE values, one can suspect that the intention was in fact to support IEEE754 floating point numbers.

As one other data point, unrelated to the question: XML datatypes xs:float and xs:double do state that they are based on IEEE754 floating point numbers, and they do support the representation of these three special values (See W3C XSD 1.0 Part 2, Datatypes).

Could you adapt the null object pattern, and in your JSON represent such values as

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

Then when checking, you can check for the type

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

I know in Java you can override serialization methods in order to implement such a thing. Not sure where your serializing from, so I can't give details on how to implement it in the serialization methods.

The strings "Infinity", "-Infinity", and "NaN" all coerce to the expected values in JS. So I'd argue the right way to represent these values in JSON is as strings.

> +"Infinity"
Infinity

> +"-Infinity"
-Infinity

> +"NaN"
NaN

It's just a shame JSON.stringify doesn't do this by default. But there is a way:

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

The reason is stated on page ii in Standard ECMA-404 The JSON Data Interchange Syntax, 1st Edition

JSON is agnostic about numbers. In any programming language, there can be a variety of number types of various capacities and complements, fixed or floating, binary or decimal. That can make interchange between different programming languages difficult. JSON instead offers only the representation of numbers that humans use: a sequence of digits. All programming languages know how to make sense of digit sequences even if they disagree on internal representations. That is enough to allow interchange.

The reason is not, as many have claimed, due to the representations of NaN and Infinity ECMA script. Simplicity is a core design principle of JSON.

Because it is so simple, it is not expected that the JSON grammar will ever change. This gives JSON, as a foundational notation, tremendous stability

If you have access to the serialization code you might represent Infinity as 1.0e+1024. The exponent is too large to represent in a double and when deserialized this is represented as Infinity. Works on webkit, unsure about other json parsers!

Potential work-around for cases like {"key":Infinity}:

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

The general idea is to replace occurences of invalid values with a string we will recognize when parsing and replace it back with the appropriate JavaScript representation.

JSON5 allows standard Javascript notation for positive and negative infinity, NaN, and numerous other things that are valid ECMAScript that were left out of JSON (trailing commas, etc.).

https://json5.org/

This makes JSON a much more useful format.

However, whether using JSON or JSON5: for security reasons, always always parse -- don't evaluate!!

The current IEEE Std 754-2008 includes definitions for two different 64-bit floating-point representations: a decimal 64-bit floating-point type and a binary 64-bit floating-point type.

After rounding the string .99999990000000006 is the same as .9999999 in the IEEE binary 64-bit representation but it is NOT the same as .9999999 in the IEEE decimal 64-bit representation. In 64-bit IEEE decimal floating-point .99999990000000006 rounds to the value .9999999000000001 which is not the same as the decimal .9999999 value.

Since JSON just treats numeric values as numeric strings of decimal digits there is no way for a system that supports both IEEE binary and decimal floating-point representations (such as IBM Power) to determine which of the two possible IEEE numeric floating-point values is intended.

If like me you have no control over the serialisation code, you can deal with NaN values by replacing them with null or any other value as a bit of a hack as follows:

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

In essence, .fail will get called when the original json parser detects an invalid token. Then a string replace is used to replace the invalid tokens. In my case it is an exception for the serialiser to return NaN values so this method is the best approach. If results normally contain invalid token you would be better off not to use $.get but instead to manually retrieve the JSON result and always run the string replacement.

本文标签: javascriptJSON left out Infinity and NaN JSON status in ECMAScriptStack Overflow