views:

574

answers:

3

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 at the top of p. 197:

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

+4  A: 

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.isNaN) {/* do that*/}
else if (myObj.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.

Zoidberg
hmmm... that's an answer to a workaround; I wasn't really asking for a workaround but rather for why these values were excluding. But +1 anyway.
Jason S
As for the why, I am not sure why they would leave these out. Perhaps they expect you to use the max and min values for the particular number, such as Integer.MAX_VALUE. Up until I.E. 5.5 they didn't even have a null keyword in javascript (at least for IE which sucks), you couldn't even use the undefined keyword, which made javascript so much more difficult. This may be the same thing, perhaps browsers will adopt this more in the future.
Zoidberg
@Zoidberg: `undefined` isn't a keyword, it's a property on the global object
olliej
@olliej: if (myVar == undefined) and undefined is in the global object? I don't understand, can you elaborate?
Zoidberg
@Zoidberg: undefined is a property on the global object -- it's not a keyword, so `"undefined" in this` returns true in the global scope. It also means you can do `undefined = 42` and `if (myVar == undefined)` becomes (essentially) `myVar == 42`.This harks back to the early days of ecmascript nee javascript where `undefined` didn't exist by default, so people just did `var undefined` in the global scope. Consequently `undefined` couldn't be made a keyword without breaking existing sites, and so we were doomed for all time to have undefined be a normal property.
olliej
@olliej: Cool, I had no idea, that actually makes complete sense. Does the same go for null?
Zoidberg
@olliej: I have no idea why you think undefined is a property on the global object. By default the lookup of undefined is the built-in value of undefined. If you override it with "undefined=42" then when you access undefined as a variable lookup, you get the overridden value. But try doing "zz=undefined; undefined=42; x={}; 'undefined old='+(x.a === zz)+', undefined new='+(x.a === undefined)". You can never redefine the internal values of null, undefined, NaN, or Infinity, even if you can override their symbol lookups.
Jason S
@Jason `undefined` is a global property because it is specified as such. Consult 15.1.1.3 of ECMAScript-262 3rd ed.
kangax
Perhaps this would make a good question...
Zoidberg
+3  A: 

Could it be because JSON is intended to be a data interchange format that can be used in a variety of platforms and allowing NaN/Infinity would make it less portable?

Ates Goral
I'm pretty sure that's the exact rationale.
kangax
NaN and Inf are not JS specific. They are IEEE standards. That should count for something. I'd say it's an unfortunate omission, and maybe a good argument for why NaN and Infinity should be immutable.
cbare
+7  A: 

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.

olliej
Where does it say Infinity and NaN are keywords? They are Javascript double values.
Jason S
If you evaluate 1/0 you get Infinity, if you evaluate -1/0 you get -Infinity, if you evaluate 0/0 you get NaN.
Jason S
But the terms `NaN` and `Infinity` are property names, so while String(1/0) produces a string `"Infinity"` that is just the string representation of the value infinity.It is not possible to represent either `NaN` or `Infinity` as literal values is ES -- you either have to use an expression (eg. 1/0, 0/0 etc) or a property lookup (referring to `Infinity` or `NaN`). As those require code execution they cannot be included in JSON.
olliej
I like how you chose to vote down the only answer that says _why_ NaN and Infinity aren't allowed - JSON is specifically designed to be a subset of the ES language that only allows literals, and any property access is by definition not a literal.
olliej
Try running jsdb or Firebug console: NaN={toString: function() { return "fiddledeedee" }} and then run 0/0 you get the internal NaN object; can't redefine it.
Jason S
NaN and Infinity are not property names (maybe they are in IE's JScript).
Jason S
To your point about safety/security, all a decent JSON parser would have to do when it goes to convert NaN is to yield the value 0/0 (rather than evaluating the symbol NaN) which will return the "real" NaN regardless of what the symbol NaN is redefined as.
Jason S
@Jason-S: 0/0 does not return an "internal NaN object" -- you are failing to understand what is going on. if i do `0/0` i now have a number with the value "not a number". If i want to display it (eg. `alert(0/0)` or whatever) then you need to convert the number to a string this is done by the ES internal function [[ToString]]. For a number [[ToString]] is defined as return the strings "-Infinity", "Infinity" or "NaN" as appropriate: you are not seeing some magical new object, you are seeing the string representation of the number.
olliej
@Jason S: In your example you're being needlessly copmlicated. `NaN=4; alert(NaN == 4)`.The point about JSON security is that it is a subset of the ES syntax that only allows literals -- if it allows `NaN` or `Infinity` it is no longer restricting to just literals, and therefore can lead to code execution, and therefore may be unsafe.
olliej
@Jason S: You can't say "JSON parser can just restrict it" because then a) JSON would no longer be a subset of ES, and b) there is sufficient code out there using a simple `eval` call to parse JSON, that allowing JSON to include non-literals would mean valid JSON could result in code execution.NaN and Infinity are property names, in your js console of choice do `"NaN" in this` and `"Infinity" in this` or more brutally `Infinity = 0; alert(Infinity == 1/0)`, etc. `Infinity` and `NaN` are properties on the global object that refer to numbers with the value infinity, or aren't a number
olliej
@Jason S: If you continue to not believe me look at the ES spec -- see section 15.1.1 of either the ECMA262-3 or ECMA262-5 specifications ("Value Properties of the Global Object")
olliej
@olliej: "NaN=4; alert(NaN == 4)" is needlessly simple; the value of the symbol NaN (which defaults to the built-in value NaN), if redefined this way, has nothing to do with the built-in value of NaN. We are at a conversational impasse.... I do understand your point about JSON strings no longer being able to be safely parsed using a simple call to "eval()". But a simple call to eval() is not safe, and if a JSON parser is smart enough to restrict string contents before calling eval() then it's smart enough to replace "NaN" with "(0/0)", and "Infinity" with "(1/0)" which would solve the problem.
Jason S
FWIW you did put some thought into your original answer, which I now understand... apologies for the downvote and if you make a token edit (just add a space or something) I'll undo the downvote.
Jason S
Where do you want a space? it seems fairly clear to me (but then i work on one of the ES implementations so maybe i'm not aware of ambiguity that is present if people don't know how JS engines work)
olliej
this site doesn't let you remove downvotes unless the post is edited, that's all
Jason S
Really? I had never realised :-(
olliej