microsoft / cpprestsdk

The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.
Other
7.95k stars 1.64k forks source link

web::json module changed the type from double to int while the double value is like 0.0, 1.0, 2.0 etc. #180

Open JasonYang-MSFT opened 8 years ago

JasonYang-MSFT commented 8 years ago

While we use web::json::value::object contains double value such as 8.0, the serializing result would be 8. After deserialization, it become an integer with value 8, not a double as it should be.

Sample input: string key = "DoubleProperty"; double value = 8.0; Sample serialization: "{"DoubleProperty":8}" Sample deserialization: string key = "DoubleProperty"; int value = 8;

Sample code is followed:

include

include "cpprest\json.h"

int main() { std::vector<std::pair<utility::string_t, web::json::value> > fields; fields.reserve(1); double double_value = 8.0; web::json::value property_value = web::json::value(double_value); fields.push_back(std::make_pair(_XPLATSTR("DoubleProperty"), std::move(property_value)));

web::json::value input = web::json::value::object(fields);
utility::string_t message = input.serialize();

ucout << message << std::endl;

web::json::value output = web::json::value::parse(message);

bool test_result = output.as_object().find(_XPLATSTR("DoubleProperty"))->second.is_double();
if (!test_result)
{
    std::cout << "Double Test Fail" << std::endl;
}
return 0;

}

After a short investigation, we find out that the problem is in serialization. Deserialization could correctly parse 8.0 as a double value, but serialization wrongly serialize 8.0 as 8.

The code might cause this problem is followed: Windows: https://github.com/Microsoft/cpprestsdk/blob/master/Release/src/json/json_serialization.cpp#L248 Linux: https://github.com/Microsoft/cpprestsdk/blob/master/Release/src/json/json_serialization.cpp#L201

The main cause should be the format string("%.*g") use in _swprintf_s_l for Windows and snprintf for Linux. As the description in https://msdn.microsoft.com/zh-cn/library/0ecbz014.aspx, it will lose any trailing zeros which cause 8.0 printed as 8

ras0219-msft commented 8 years ago

Could you go into a bit more detail about the problems this causes for your application? Why do you specifically need the number '8' to be serialized as '8.0' instead of '8'?

If this is to ease manipulation with another cpprestsdk-using program, we have an API value::is_number() which will check if the parsed value is any numeric type (int, double, or otherwise). The function value::as_double() will convert any internal number type into a double for your program.

JasonYang-MSFT commented 8 years ago

@ras0219-msft We use this to store data in a NoSQL database through network. Doubles were serialized as string. So serialize 8.0 as 8 will cause a total different result.

alantang-MSFT commented 6 years ago

This is due to the format specifier used for formatting numbers:

"%.*g"

The number 1.0 will be serialized as "1", which will be interpreted as an integer when you try to read it back. I know it's possible to use is_number() and then as_double() or as_int(), but this requires us to make assumptions in our code when it should've been possible to determine data type from the json string alone. That is, if 1.0 was correctly serialized as "1.0", then we can avoid having to do an int->double conversion.

You get:

and it's impossible to determine if the int should be a double or an integer without encoding additional information elsewhere in the JSON (awful).

We're actually blocked on this bug and may need to explore using other JSON libs that don't have this limitation.

zoenie123 commented 7 months ago

When is something going to be done about this bug? I am also running into this issue. It's important that doubles stay doubles with digits and that integers stay integers without digits after the serialisation!