danielaparker / jsoncons

A C++, header-only library for constructing JSON and JSON-like data formats, with JSON Pointer, JSON Patch, JSON Schema, JSONPath, JMESPath, CSV, MessagePack, CBOR, BSON, UBJSON
https://danielaparker.github.io/jsoncons
Other
715 stars 163 forks source link

Writing float results in '.0' written #96

Closed rychale closed 8 years ago

rychale commented 8 years ago

When writing json object into a stream, it writes '.0' instead of actual float.

Unable to read json from a stream containing '.0' as a field's value (probably this is a correct behavior).

#include <iostream>
#include <fstream>

#include "jsoncons/json.hpp"

int main(int argc, char** argv)
{
        try
        {
                jsoncons::json out;
                out["a"] = 1.0;

                std::ofstream of("jsontest.json");
                of << out << std::endl;

                jsoncons::json in = jsoncons::json::parse_file("jsontest.json");
                std::cout << in << std::endl;
        }
        catch(const std::exception& e)
        {
                std::cerr << e.what() << std::endl;
        }

        return 0;
}
danielaparker commented 8 years ago

If I run the example above, it fails, but because the ofstream buffer has not been flushed before the file has been read. If I rewrite it explicitly closing the ofstream,

int main(int argc, char** argv)
{
        try
        {
                jsoncons::json out;
                out["a"] = 1.0;

                std::ofstream of("jsontest.json");
                of << out << std::endl;
                of.close();

                jsoncons::json in = jsoncons::json::parse_file("jsontest.json");
                std::cout << in << std::endl;
        }
        catch(const std::exception& e)
        {
                std::cerr << e.what() << std::endl;
        }

        return 0;
}

I get the expected output {"a":1.0}

Could you test again with the revised example?

Thanks, Daniel

rychale commented 8 years ago

Sounds strange, because AFAIK std::endl calls flush by itself.

Anyway, with or without explicit call of of.close(); I get following:

[arychkov@zzz benchmark]$ cat jsontest.json 
{"a":.0}
danielaparker commented 8 years ago

I understand from your other issue that you've ran the test suite. Were any failures reported? There is test coverage of double-to-string conversions, including 0, 1, 0.1

danielaparker commented 8 years ago

Another question: if you change the example to

out["a"] = 1.1;

what is written to the file?

rychale commented 8 years ago

I've run the tests on two platforms. It works fine with gcc 5.4.0 and boost 1.58.0, but when running the tests with gcc 6.2.0 and boost 1.60 there are 34 tests failed including double_round_trip_test_suite:

Running 284 test cases...
[
    {
        "bool-f": null,
        "float-f": null,
        "int-f": null,
        "string-f": ""
    },
    {
        "bool-f": true,
        "float-f": .0,
        "int-f": 12,
        "string-f": "test string"
    },
    {
        "bool-f": null,
        "float-f": null,
        "int-f": null,
        "string-f": ""
    }
]
[
    {
        "bool-f": false,
        "float-f": .0,
        "int-f": 0,
        "string-f": ""
    },
    {
        "bool-f": true,
        "float-f": .0,
        "int-f": 12,
        "string-f": "test string"
    },
    {
        "bool-f": false,
        "float-f": .0,
        "int-f": 0,
        "string-f": ""
    }
]
[
    {
        "bool-f": null,
        "float-f": null,
        "int-f": null,
        "string-f": ""
    },
    {
        "bool-f": true,
        "float-f": .0,
        "int-f": 12,
        "string-f": "test string"
    },
    {
        "bool-f": null,
        "float-f": null,
        "int-f": null,
        "string-f": ""
    }
]
[
    {
        "a": "1"
    },
    {
        "a": "4"
    }
]
[
    {
        "a": "1",
        "b": "2",
        "c": "3"
    },
    {
        "a": "4",
        "b": "5",
        "c": "6"
    }
]
[
    {
        "country_code": "ABW",
        "name": "ARUBA"
    },
    {
        "country_code": "ATF",
        "name": "FRENCH SOUTHERN TERRITORIES, D.R. OF"
    },
    {
        "country_code": "VUT",
        "name": "VANUATU"
    },
    {
        "country_code": "WLF",
        "name": "WALLIS & FUTUNA ISLANDS"
    }
]
[
    {
        "Country Code": "ABW",
        "Name": "ARUBA"
    },
    {
        "Country Code": "ATF",
        "Name": "FRENCH SOUTHERN TERRITORIES, D.R. OF"
    },
    {
        "Country Code": "VUT",
        "Name": "VANUATU"
    },
    {
        "Country Code": "WLF",
        "Name": "WALLIS & FUTUNA ISLANDS"
    }
]
country_code,name
ABW,ARUBA
ATF,"FRENCH SOUTHERN TERRITORIES, D.R. OF"
VUT,VANUATU
WLF,WALLIS & FUTUNA ISLANDS
[
    {
        "dept": "sales",
        "employee-name": "Smith, Matthew",
        "employee-no": "00000001",
        "note": "",
        "salary": "150,000.00"
    },
    {
        "dept": "sales",
        "employee-name": "Brown, Sarah",
        "employee-no": "00000002",
        "note": "",
        "salary": "89,000.00"
    },
    {
        "dept": "finance",
        "employee-name": "Oberc, Scott",
        "employee-no": "00000003",
        "note": "",
        "salary": "110,000.00"
    },
    {
        "dept": "sales",
        "employee-name": "Scott, Colette",
        "employee-no": "00000004",
        "note": "\"Exemplary\" employee\nDependable, trustworthy",
        "salary": "75,000.00"
    }
]
dept    employee-name   employee-no note    salary

sales   Brown, Sarah    00000002        89,000.00
finance Oberc, Scott    00000003        110,000.00
sales   Scott, Colette  00000004    ""Exemplary"" employee
Dependable, trustworthy 75,000.00
/home/arychkov/git/jsoncons/test_suite/src/double_round_trip_tests.cpp(69): error: in "double_round_trip_test_suite/test_round_trip": check input == json::parse(input).as<std::string>() has failed [42.229999999999997 != .0]
/home/arychkov/git/jsoncons/test_suite/src/double_round_trip_tests.cpp(72): error: in "double_round_trip_test_suite/test_round_trip": check input == json::parse(input).as<std::string>() has failed [9.0099999999999998 != .0]
/home/arychkov/git/jsoncons/test_suite/src/double_round_trip_tests.cpp(75): error: in "double_round_trip_test_suite/test_round_trip": check input == json::parse(input).as<std::string>() has failed [13.449999999999999 != .0]
1e+100: .0
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(41): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("1.0e+100") || s == std::string("1.0e100") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(45): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("1.0e-100") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(49): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("1.23456789e-101") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(53): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("1.23456789e+99") || s == std::string("1.23456789e+099") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(57): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("1.23456e+6") || s == std::string("1.23456e+06") || s == std::string("1.23456e+006") || s == std::string("1234560") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(61): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("1.23456e-7") || s == std::string("1.23456e-07") || s == std::string("1.23456e-007") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(65): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("-1.0e+100") || s == std::string("-1.0e100") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(69): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("-1.0e-100") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(73): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("0.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(77): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("0.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(81): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("1.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(85): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("0.1") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(89): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("1.1000000000000001") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(93): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("-1.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(97): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("10.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(101): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("-10.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(105): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("11.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(109): error: in "double_to_string_test_suite/test_double_to_string": check s == std::string("-11.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(116): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"1.0e+100") || s == std::wstring(L"1.0e100") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(120): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"1.0e-100") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(124): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"-1.0e+100") || s == std::wstring(L"-1.0e100") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(128): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"-1.0e-100") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(132): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"0.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(136): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"0.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(140): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"1.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(144): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"-1.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(148): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"10.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(152): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"-10.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(156): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"11.0") has failed
/home/arychkov/git/jsoncons/test_suite/src/double_to_string_tests.cpp(160): error: in "double_to_string_test_suite/test_double_to_wstring": check s == std::wstring(L"-11.0") has failed
[1,2,3]
{"first":1,"second":2}
{"data":{"id":[0,1,2,3,4,5,6,7],"item":[{"first":1,"second":2}]}}
{"events_attended":10,"first_name":"Jane","last_name":"Roe"}
{
    "members": [
        {
            "accept_waiver_of_liability": true,
            "events_attended": 10,
            "first_name": "Jane",
            "last_name": "Roe"
        },
        {
            "accept_waiver_of_liability": true,
            "events_attended": 2,
            "first_name": "John",
            "last_name": "Doe"
        }
    ]
}
[
    null,
    true,
    .0,
    {
        "accept_waiver_of_liability": true,
        "events_attended": 10,
        "first_name": "Jane",
        "last_name": "Roe"
    }
]
[10,20,30]
[.0,.0,.0]
Jane Roe, 10, 1
outdoor_experience not found
experience=, first_aid_certification=0
name=accept_waiver_of_liability, value=true
name=events_attended, value=10
name=first_name, value=Jane
name=last_name, value=Roe
Montreal
Toronto
Ottawa
Vancouver
{"max_longlong":-9223372036854775807}
TEST LIMITS
{"max_longlong_overflow":-92233720368547758070}
{"max_longlong_overflow":.0}
{"max_ulonglong":18446744073709551615}
{"max_ulonglong_overflow":.0}
{"a":1,"b":2,"c":3}{"a":4,"b":5,"c":6}
{
    "maturity": "2015-01-01",
    "observation_dates": ["2013-10-21","2013-10-28"]
}
Maturity: 2014-Oct-14

Observation dates: 

2014-Feb-14
2014-Feb-21

{
    "Maturity": "2014-10-14",
    "ObservationDates": ["2014-02-14","2014-02-21"]
}
{"field1":
"value}
[
    [.0,.0],
    [.0,.0]
]1
1
1
/home/arychkov/git/jsoncons/test_suite/src/json_type_traits_stl_tests.cpp(268): error: in "json_type_traits_stl_suite/test_from_stl_container": check true == j_ummap.find("three")->value().as<bool>() has failed [true != false]
{"one":true,"three":false,"two":true}
16
EXAMPLE
{"getValuesReturn" : {"return" : "true","TextTag" : "Text!","String" : ["First item","Second item","Third item"],"TagWithAttrsAndText" : {"content" : "Text!","attr3" : "value3","attr2" : "value2","attr1" : "value1"},"EmptyTag" : true,"attribute" : {"attrValue" : "value"},"TagWithAttrs" : {"attr3" : "value3","attr2" : "value2","attr1" : "value1"}}}
{"getValuesReturn":{"EmptyTag":true,"String":["First item","Second item","Third item"],"TagWithAttrs":{"attr1":"value1","attr2":"value2","attr3":"value3"},"TagWithAttrsAndText":{"attr1":"value1","attr2":"value2","attr3":"value3","content":"Text!"},"TextTag":"Text!","attribute":{"attrValue":"value"},"return":"true"}}
{"field":null}
test_to_string
{"string":"value","null":null,"bool1":false,"bool2":true,"integer":12345678,"neg-integer":-87654321,"double":123456.01,"neg-double":-654321.01,"exp":2.00600e+03,"minus-exp":1.00600e-010,"escaped-string":"\\\n"}
{"bool1":false,"bool2":true,"double":.0,"escaped-string":"\\\n","exp":.0,"integer":12345678,"minus-exp":.0,"neg-double":.0,"neg-integer":-87654321,"null":null,"string":"value"}
{
    "city": "Toronto",
    "number": .0
}
[100,200,300,400]
{"field1":null,"field2":null,"field3":.0}
{
    "field1": null,
    "field2": 1e9999,
    "field3": .0
}
{"field1":.0,"field2":.0,"field3":.0}
city=Toronto
country=Canada
province=Ontario
Toronto
Vancouver
Montreal
Input:    ["\u0040\u0040\u0000\u0011"]
Hex dump: [0x40 0x40 0x00 0x11]
Output:   ["@@\u0000\u0011"]
{"persons":["John","David"]}
Input:    ["\u007F\u07FF\u0800"]
Hex dump: [0x7f 0xdf 0xbf 0xe0 0xa0 0x80]
Output:   ["\u007F\u07FF\u0800"]
Hex dump: [0x7f 0xdf 0xbf 0xe0 0xa0 0x80]
{
    "persons": [
        {
            "address": {
                "city": "Toronto",
                "country": "Canada"
            },
            "birth_date": "1972-01-30",
            "first_name": "John",
            "last_name": "Smith"
        }
    ]
}
x1=1
x2=20
{
    "store": {
        "bicycle": {
            "color": "red",
            "price": .0
        },
        "book": [
            {
                "author": "Nigel Rees",
                "category": "reference",
                "price": .0,
                "title": "Sayings of the Century"
            },
            {
                "author": "Evelyn Waugh",
                "category": "fiction",
                "price": .0,
                "title": "Sword of Honour"
            },
            {
                "author": "Herman Melville",
                "category": "fiction",
                "isbn": "0-553-21311-3",
                "price": .0,
                "title": "Moby Dick"
            },
            {
                "author": "J. R. R. Tolkien",
                "category": "fiction",
                "isbn": "0-395-19395-8",
                "price": .0,
                "title": "The Lord of the Rings"
            }
        ]
    }
}
$..book[(@.length-1)]
4
0
"json.json"
.json "input/JSONPath/query1.json"
.jsonpath "query1"
.json "input/JSONPath/query2.json"
.jsonpath "query2"
.json "input/JSONPath/query3.json"
.jsonpath "query3"
.json "input/JSONPath/query4.json"
.jsonpath "query4"
.json "input/JSONPath/query5.json"
.jsonpath "query5"
.json "input/JSONPath/query6.json"
.jsonpath "query6"
.json "input/JSONPath/query7.json"
.jsonpath "query7"
.json "input/JSONPath/query8.json"
.jsonpath "query8"
.json "input/JSONPath/query9.json"
.jsonpath "query9"
$['store']['book']
$..['bicycle'][*].price

$..['bicycle']..price
$..bicycle[?(@.price>=97)]
$..book[1:4:2].author
$..book[1:4:2,0].author
$..book[(@.length - 2)]
$..book[-1:]
$..book[*]['author','title']
[
    "Nigel Rees",
    "Sayings of the Century",
    "Evelyn Waugh",
    "Sword of Honour",
    "Herman Melville",
    "Moby Dick",
    "J. R. R. Tolkien",
    "The Lord of the Rings"
]
"reference"
[
    "Nigel Rees",
    "Herman Melville"
]
<?xml version="1.0" encoding="UTF-8"?>
<json:object xsi:schemaLocation="http://www.datapower.com/schemas/json jsonx.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:json="http://www.ibm.com/xmlns/prod/2009/jsonx">
    <json:null name="additionalInfo">null</json:null>
    <json:object name="address">
        <json:string name="city">New York</json:string>
        <json:number name="postalCode">10021</json:number>
        <json:string name="state">NY</json:string>
        <json:string name="streetAddress">21 2nd Street</json:string>
    </json:object>
    <json:string name="ficoScore"> > 640</json:string>
    <json:number name="height">.0</json:number>
    <json:string name="name">John Smith</json:string>
    <json:array name="phoneNumbers">
        <json:string>212 555-1111</json:string>
        <json:string>212 555-2222</json:string>
    </json:array>
    <json:boolean name="remote">false</json:boolean>
</json:object>
<?xml version="1.0" encoding="UTF-8"?>
<json:object xsi:schemaLocation="http://www.datapower.com/schemas/json jsonx.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:json="http://www.ibm.com/xmlns/prod/2009/jsonx">
    <json:string name="&amp;">&amp;</json:string>
    <json:string name="&lt;">&lt;</json:string>
    <json:string name=">">></json:string>
    <json:string name="&#34;">"</json:string>
    <json:string name="'">'</json:string>
    <json:string name="&#x00A9;"></json:string>
</json:object>
{
    "street_number": "100",
    "street_name": "Queen St W",
    "city": "Toronto",
    "country": "Canada"
}
{
    "street_number": "100",
    "street_name": "Queen St W",
    "city": "Toronto",
    "country": "Canada",
    "postal_code": "M5H 2N2"
}
{
    "street_number": "100",
    "street_name": "Queen St W",
    "city": "Toronto",
    "province": "Ontario",
    "country": "Canada",
    "postal_code": "M5H 2N2",
    "unit_type": "O"
}
{
    "street_number": "100",
    "street_name": "Queen St W",
    "city": "Toronto",
    "province": "Ontario",
    "country": "Canada",
    "postal_code": "M5H 2N2"
}
Wide character test
{"field1":"test","field2":.0,"field3":true}

*** 34 failures are detected in the test module "Master Test Suite"
danielaparker commented 8 years ago

Okay, it looks like with gcc 6.2.0, in float_printer, in jsoncons_io.hpp, vs_.length() must be always returning zero, to explain all floating point numbers being printed as .0. There appears to be something about jsoncons::basic_ovectorstream (which is a custom implementation std::basic_ostream) that gcc 6.2.0 doesn't like. I'll have a look at it.

danielaparker commented 8 years ago

I replaced the use of jsoncons::basic_ovectorstream with std::basic_stringstream. Could you check if tests now pass with gcc 6.2.0?

rychale commented 8 years ago
In file included from /home/arychkov/git/jsoncons/src/jsoncons/jsoncons_io.hpp:23:0,
                 from /home/arychkov/git/jsoncons/src/jsoncons/jsoncons.hpp:19,
                 from /home/arychkov/git/jsoncons/src/jsoncons/output_format.hpp:18,
                 from /home/arychkov/git/jsoncons/src/jsoncons/json_traits.hpp:15,
                 from /home/arychkov/git/jsoncons/src/jsoncons/json.hpp:19,
                 from jsontest.cpp:4:
/home/arychkov/git/jsoncons/src/jsoncons/ovectorstream.hpp: In constructor ‘jsoncons::basic_ovectorstream<CharT, Traits>::basic_ovectorstream(std::size_t)’:
/home/arychkov/git/jsoncons/src/jsoncons/ovectorstream.hpp:199:22: error: there are no arguments to ‘init_ovectorbuf’ that depend on a template parameter, so a declaration of ‘init_ovectorbuf’ must be available [-fpermissive]
     {init_ovectorbuf();}
danielaparker commented 8 years ago

Try again?

rychale commented 8 years ago

It is much better now.

There is only one test in the test_suite which is still failing: /home/arychkov/git/jsoncons/test_suite/src/json_type_traits_stl_tests.cpp(268): error: in "json_type_traits_stl_suite/test_from_stl_container": check true == j_ummap.find("three")->value().as<bool>() has failed [true != false]

The test with floats I've originally posted works perfect now.

danielaparker commented 8 years ago

Okay, I'll need to revisit the multimap to json assignment sometime, but for now it doesn't matter, so I've simplified the test. I've also added a std::vector<unsigned long> tojson test, just to verify that the elsewhere reported difficulty with std::array was because of std::array and not it's template parameter. Hopefully gcc 6.2.0 will be okay with that.

rychale commented 8 years ago

No errors detected.

danielaparker commented 8 years ago

I found the issue with the original jsoncons::basic_ovectorstream and gcc 6.2.0 (tested on ubuntu). It's fixed. I've reverted back to that code (now fixed) on master. Thanks again for reporting this. Going forward, I'll be doing more testing with gcc 6.2.0 before promoting to master.