9

I'm looking into the feasibility of adding NaN/Infinity support to a pre-existing scientific application that uses JSONRPC for client/server interactions. Many JSON libs do handle (optionally in some cases) NaNs and Infs, for example:

  • Python json reads and writes
  • Java Jackson reads but writes strings instead of barewords
  • Java GSON reads and writes
  • Javascript can read

I'm aware that NaN and Infinity are not supported in the JSON spec, and am aware of the related questions. However, AFAICT the question of whether there's some way of coercing the native JS JSON.stringify() method to emit NaN/Infinity or, alternately, there's a JS JSON library that does the same is unanswered. A subtle difference to the referenced questions, perhaps, but important. So far I've been unable to discover such a method or library, so here I am. Is the only option writing one's own JSON serializer?

Note that the replacement parameter of JSON.stringify() is not helpful, at least in my hands.

UPDATE: Emitting NaN/Infinity etc. as strings makes the semantics of those strings ambiguous. They need to be emitted as barewords as in the Python and GSON implementations.

Community
  • 1
  • 1
elhefe
  • 3,404
  • 3
  • 31
  • 45
  • 1
    This is exactly a `replacement` parameter job, it's not so difficult. Perhaps you can specify more about your actual problem, along with an example and sample data? – Xotic750 Feb 20 '14 at 02:48
  • JSON.stringify({a:1,b:1/"gg", c:{d:5}}, function(a,b,c){if(Object.is(NaN, b)){return "NaN";} return b;}) – dandavis Feb 20 '14 at 02:50
  • @dandavis see the update above. – elhefe Feb 20 '14 at 05:05
  • If you're going to downvote the question, at least explain what's wrong with it so I can fix it. – elhefe Feb 20 '14 at 06:25
  • 1
    you can use a guid instead of, ex:"NaN" if you're worried about string collisions and want the use the fast native parser. – dandavis Feb 20 '14 at 08:18
  • @dandavis yeah, but then you lose the human readability of json and anyone examining the Json will likely get confused. Seems a bit too hacky to me. – elhefe Feb 20 '14 at 16:58

2 Answers2

6

Here is an example

Javascript

var array1 = [-Infinity, -1, 0, 1, 2, NaN, 4, 5, Infinity],
    json = JSON.stringify(array1, function (key, value) {
        if (value !== value) {
            return 'NaN';
        }

        if (value === Infinity) {
            return 'Infinity';
        }

        if (value === -Infinity) {
            return '-Infinity';
        }

        return value;
    }),
    array2 = JSON.parse(json, function (key, value) {
        if (value === 'NaN') {
            return NaN;
        }

        if (value === 'Infinity') {
            return Infinity;
        }

        if (value === '-Infinity') {
            return -Infinity;
        }

        return value;
    });

console.log(json);
console.log(array2);

Output

["-Infinity",-1,0,1,2,"NaN",4,5,"Infinity"]
[-Infinity, -1, 0, 1, 2, NaN, 4, 5, Infinity]

References

JSON.stringify

JSON.parse

On jsFiddle

Update:

Javascript

var array1 = [-Infinity, -1, 0, 1, 2, NaN, 4, 5, Infinity],
    json = JSON.stringify(array1, function (key, value) {
        if (value !== value) {
            return '0/0';
        }

        if (value === 1/0) {
            return '1/0';
        }

        if (value === -1/0) {
            return '-1/0';
        }

        return value;
    }),
    array2 = JSON.parse(json, function (key, value) {
        if (value === '0/0') {
            return 0/0;
        }

        if (value === '1/0') {
            return Infinity;
        }

        if (value === '-1/0') {
            return -1/0;
        }

        return value;
    });

console.log(json);
console.log(array2);

Output

["-1/0",-1,0,1,2,"0/0",4,5,"1/0"]
[-Infinity, -1, 0, 1, 2, NaN, 4, 5, Infinity] 

On jsFiddle

Xotic750
  • 22,914
  • 8
  • 57
  • 79
  • 1
    See update above - the semantics of the strings you use are now ambigious. – elhefe Feb 20 '14 at 05:06
  • How are they [ambiguous](http://en.wikipedia.org/wiki/Ambiguity)? You haven't provided us with any example a data. The example that I used is numbers only. Therefore these string representations in this context are not ambiguous, they only represent those specific values. – Xotic750 Feb 20 '14 at 05:31
  • If you want a library that does not comply to specification, I would suggest that you get a copy of ['JSON2'](https://github.com/douglascrockford/JSON-js) and modify the code to suit your needs. – Xotic750 Feb 20 '14 at 05:39
  • Or if it is the string representations that you are worried about and want an alternative then try `If you evaluate 1/0 you get Infinity, if you evaluate -1/0 you get -Infinity, if you evaluate 0/0 you get NaN.` – Xotic750 Feb 20 '14 at 05:42
  • Because, for example, the string Infinity now has two possible meanings - the string Infinity and the numeric representation of infinity, and there's no way for the receiver to know which meaning to choose. There's no way for me to provide sample data that represents all the cases that could occur - it could be virtually any object. – elhefe Feb 20 '14 at 06:20
  • yeah, hacking code is what I'm hoping to avoid, but it's a last resort. I'd obviously rather use pre-existing, well used & tested libraries. – elhefe Feb 20 '14 at 06:21
  • I'm afraid I don't understand your 3rd comment. How would you implement this? – elhefe Feb 20 '14 at 06:22
  • If you do not have control over the receiver then how are you going to get them to use a non standard spec JSON, if not by through agreement? Otherwise you can just as easily agree what strings will represent said values. – Xotic750 Feb 20 '14 at 06:33
  • I do have control over the receiver - I don't have control over the contents of the JSON. That means that semantic additions to JSON (e.g. the barewords NaN, Infinity etc [which are how most of the libs that do handle NaN, Infinity have implemented it]) are ok, because they won't alter data. Changing the meanings of arbitrary strings can alter data. – elhefe Feb 20 '14 at 06:36
  • Then you have no choice but to modify one of the existing libraries, `JSON`, `JSON2` or `JSON3` to handle those raw, and then push your custom library out to your clients or whatever. It isn't so hard to do. – Xotic750 Feb 20 '14 at 06:39
  • Very minor changes to `JSON2` and now you have a non standard `JSON` library that gives you barewords: http://jsfiddle.net/Xotic750/mswgn/ Outputs `[-Infinity,-1,0,1,2,NaN,4,5,Infinity]` and `[-Infinity, -1, 0, 1, 2, NaN, 4, 5, Infinity]` – Xotic750 Feb 20 '14 at 06:59
  • Heh, I must've been looking at the code at the same time you were, and yeah, the hack is trivial. JSON3 looks just as simple to hack. – elhefe Feb 20 '14 at 07:01
0

replacement parameter of JSON.stringify is quite right tool for the job.

JSON.stringify(data, function(key, value) {
   if (value === Infinity) {
      return "Infinity";
   } else if (value === -Infinity) {
      return "-Infinity";
   } else if (value !== value) {
      return "NaN";
   }
   return value;
});

And on the other side you can use reviver parameter of JSON.parse.

 JSON.parse(data, function(key, value) {
   if (value === "Infinity") {
      return Infinity;
   } else if (value === "-Infinity") {
      return -Infinity;
   } else if (value === "NaN") {
      return NaN;
   }
   return value;
});
Michal Brašna
  • 2,293
  • 13
  • 17
  • 1
    No need for the `else`s after `return`s – Xotic750 Feb 20 '14 at 03:24
  • See the update above - the solution here introduces ambiguity into the semantics of the strings you chose to represent NaN & Inf. – elhefe Feb 20 '14 at 05:08
  • @Xotic750 I prefer the `else if`, as it makes code shorter and doesn't affect functionality in this case. But thanks for noticing. – Michal Brašna Feb 20 '14 at 11:20
  • @elhefe I agree, they are now ambigous, nothing more we can do using existing native implementation. I see, you found solution, hacking on JSON2 library. GL – Michal Brašna Feb 20 '14 at 11:23