3

I managed to find a bug (maybe not bug, but huge pain in the ass) that took an impossibly long time to track down, and can replicate it using the REPL (NodeJS):

> o = {};
{}

> JSON.stringify(o)
'{}'

> o.n = 10
10

> JSON.stringify(o)
'{"n":10}'

> o.n = Infinity;
Infinity

> JSON.stringify(o)
'{"n":null}'

> null == Infinity
false

> null === Infinity
false

> typeof 10
'number'

> typeof Infinity
'number'

When you put Infinity into JSON it becomes null, despite Infinity being a number type. Normally who cares, but when -Infinity, NaN, 0, and Infinity all have special meaning in the context of your application (machine learning), it's pretty important to be able to transmit special numbers as plain JSON without resorting to stuffing and unstuffing it into a string every time.

String stuffing requires an extra type check for every access, then a switch case string comparison against all special number types, followed by a reallocation to the actual special number type. Once isn't bad, 40 or 50 trillion times a minute is where you really start to curse the ECMA gods.

Am I missing something, or is this just one of those things ECMA considered not so important?

Adrian Seeley
  • 2,262
  • 2
  • 29
  • 37

4 Answers4

6

Apprently it was intentional:

http://www.ecma-international.org/publications/standards/Ecma-404.htm

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.

Adrian Seeley
  • 2,262
  • 2
  • 29
  • 37
  • 8
    Whover came up with this reasoning didn’t do their homework. Everything supports [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) floats, which support NaN, -0, -Inf, and Inf. – flying sheep Sep 28 '18 at 09:43
  • @flyingsheep I simply use integers in all programs at all times. – Hunter Kohler Jul 17 '22 at 14:47
4

You can't use NaN and Infinity in classic JSON, unfortunately, JSON is very limited. It has nothing to do with ECMA, JSON wasn't created by them and frankly speaking it has little in common with JavaScript.

If you want to use all javascript numbers, I suggest to serialize your data using JSON5 instead.

alex
  • 11,935
  • 3
  • 30
  • 42
  • this is currently the correct answer. The JSON standard has evolved, and the JSON5 proposal is more appropriate atm. – episodeyang Mar 04 '19 at 23:14
3

That's correct, JSON cannot represent all JavaScript values:

(quoted from JavaScript Definitive Guide, 6th editon, O'Reilly, p.138):

JSON syntax is a subset of JavaScript syntax, and it cannot represent all JavaScript values. Objects, arrays, strings, finite numbers, true, false, and null are supported and can be serialized and restored. NaN, Infinity, and -Infinity are serialized to null. Date objects are serialized to ISO-formatted date strings (see the Date.toJSON() function), but JSON.parse() leaves these in string form and does not restore the original Date object. Function, RegExp, and Error objects and the undefined value cannot be serialized or restored.

nonopolarity
  • 146,324
  • 131
  • 460
  • 740
  • JSON is **not** a subset of JavaScript, see http://timelessrepo.com/json-isnt-a-javascript-subset – alex Mar 28 '14 at 00:33
1

So, we have already established that -Infinity, Infinity, and NaN are not legal JSON values. Here is my proposal for how to handle this effciently.

'n' can continue to hold either a legal JSON value, or null (for an illegal value). But, if the value is illegal, you can have a special property (that only needs to exist if 'n' is null) -- call it "altValue" -- whose possible values can be "-Infinity", "Infinity", and "NaN". You can also have an object that maps each of these string values to the corresponding Javascript value.

When accessing the "n" value, you'd need to do a null check (and presumably that will suffice in most cases). In the rare cases where 'n' is null, you just need to map the "altValue" value to the corresponding Javascript value.

For example:

var altValueMap = {
  "-Infinity": -Infinity,
  "Infinity": Infinity,
  "NaN": NaN
}

function MyData(n){
  this.n = n;
  if (!isFinite(n)) {
    this.altValue = (n === Infinity) ? 'Infinity' : (n === -Infinity) ? '-Infinity' : 'NaN';
  }
}

function getN(data) {
  return (data.n === null) ? altValueMap[data.altValue] : data.n;
}

var x = new MyData(NaN);
var y = JSON.stringify(x);
var z = JSON.parse(y);
getN(z); // Returns NaN
cybersam
  • 63,203
  • 6
  • 53
  • 76
  • I know this is old, but, is there a specific reason you use `altValueMap` instead of e.g. [`Number()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)? Is it a security issue, e.g. to prevent code injection? – djvg Aug 28 '23 at 12:46