Menu

#89 Invalid JSON serialization of NaN values

cppcms-v1.0.x
open
nobody
1
2014-10-30
2013-02-05
No

Hi,
CppCMS's JSON encoding facility encodes NaN values as -1.#QNAN (at least in my test case, with a particular data set, on MSVC 2012 / Windows). This isn't valid JSON and therefore causes client code to error out.

The applicable standard (ECMA-262) seems to specify that the correct serialization of any non-finite number values (NaNs, infinities, etc.) is null.

Discussion

  • Moti Zilberman

    Moti Zilberman - 2013-02-05

    Artyom, meaning no disrespect - CppCMS is cool, which is why I'm taking the time to do this :) - I think you went outside the JSON-relevant part of that specification.

    Here's my more detailed take on the issue:

    1) RFC 4627 says (on page 2):

    Numeric values that cannot be represented as sequences of digits
    (such as Infinity and NaN) are not permitted.

    2) ECMA 262 section 15.12.3 (the one I linked to) specifies the JSON.stringify function's behavior. I think you'll agree that this is an authoritative source on what JavaScript does in the equivalent case. JavaScript/ECMAScript has NaNs and infinities so there is indeed an exact equivalent case of "there's a NaN in my data - how does that translate to JSON?". Quoting the interesting parts:

    The abstract operation Str(key, holder) [...]
    1. Let value be the result of [...]
    [...]
    9. If Type(value) is Number
    If value is finite then return ToString(value).
    Else, return "null".

    (Emphasis mine)

    From #1 I gather that there is no JSON representation for NaNs and infinities. On its own, it would mean any implementation is free to do what it deems correct, including throwing an exception. But, in #2, JavaScript already sets an example for an implementation's behavior. And according to this example (which IMHO CppCMS should follow) it's not an error to try to serialize a NaN or Infinity to JSON - you just get a null out of it. So from the principle of least surprise (among other things), I think no exception should be thrown, at least not by default.

     
  • Moti Zilberman

    Moti Zilberman - 2013-02-05

    For the record, my quick-and-dirty fix - assuming the silent conversion to null is desired - was:

    1) Add a usable implementation of isfinite from Boost as cppcms::json::detail::isfinite_impl() (a proper solution would be to add the relevant parts of Boost.Math to cppcms_boost and/or use an external Boost installation - I don't know the exact "CppCMS way" for this)

    2) In the implementation of value::write_value() in json.cpp, change:

    case json::is_number:
        out<<std::setprecision(std::numeric_limits<double>::digits10+1)<<number();
        break;
    

    to

    case json::is_number:
        if (!cppcms::json::detail::isfinite_impl(number()))
            out<<"null";
        else   
            out<<std::setprecision(std::numeric_limits<double>::digits10+1)<<number();
        break;
    
     

Anonymous
Anonymous