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

  • Artyom Beilis

    Artyom Beilis - 2013-02-05

    Actually I'm not sure that using null is correct:

    According to the link:

     if Type(v) is Number then let item be ToString(v).
    

    From there to: http://www.ecma-international.org/ecma-262/5.1/#sec-9.8
    And then to http://www.ecma-international.org/ecma-262/5.1/#sec-9.8.1

    NaN value should be converted to NaN.

    But NaN is not part of the JSON specifications: http://www.json.org/

    So technically NaN can't be presented in JSON.

    Probably in case of NaN I should throw some kind of exception. Not sure thou.

     
  • 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

Add attachments
Cancel





Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.