From a9baab76c27704467df03bcf2a30ec463207b4b4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 28 Mar 2018 18:20:55 +0200 Subject: [PATCH 1/2] :ambulance: fix for #1021 --- include/nlohmann/detail/exceptions.hpp | 1 + .../nlohmann/detail/output/binary_writer.hpp | 50 +++++++++++++++--- single_include/nlohmann/json.hpp | 51 ++++++++++++++++--- test/src/unit-regression.cpp | 18 +++++++ 4 files changed, 108 insertions(+), 12 deletions(-) diff --git a/include/nlohmann/detail/exceptions.hpp b/include/nlohmann/detail/exceptions.hpp index b73d7b1f9..53b38ec8f 100644 --- a/include/nlohmann/detail/exceptions.hpp +++ b/include/nlohmann/detail/exceptions.hpp @@ -301,6 +301,7 @@ Exceptions have ids 5xx. name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. +json.exception.other_error.502 | type for number_float_t is not supported | The template type for a number is not supported for the binary serialization in CBOR, MessagePack or UBJSON. @sa @ref exception for the base class of the library exceptions @sa @ref parse_error for exceptions indicating a parse error diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 1b15bdafd..13968fedf 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -149,9 +149,19 @@ class binary_writer break; } - case value_t::number_float: // Double-Precision Float + case value_t::number_float: { - oa->write_character(static_cast(0xFB)); + switch (sizeof(typename BasicJsonType::number_float_t)) + { + case 4: // Single-Precision Float + oa->write_character(static_cast(0xFA)); + break; + case 8: // Double-Precision Float + oa->write_character(static_cast(0xFB)); + break; + default: + JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); + } write_number(j.m_value.number_float); break; } @@ -409,9 +419,19 @@ class binary_writer break; } - case value_t::number_float: // float 64 + case value_t::number_float: { - oa->write_character(static_cast(0xCB)); + switch (sizeof(typename BasicJsonType::number_float_t)) + { + case 4: // float 32 + oa->write_character(static_cast(0xCA)); + break; + case 8: // float 64 + oa->write_character(static_cast(0xCB)); + break; + default: + JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); + } write_number(j.m_value.number_float); break; } @@ -712,7 +732,17 @@ class binary_writer { if (add_prefix) { - oa->write_character(static_cast('D')); // float64 + switch (sizeof(typename BasicJsonType::number_float_t)) + { + case 4: // float 32 + oa->write_character(static_cast('d')); + break; + case 8: // float 64 + oa->write_character(static_cast('D')); + break; + default: + JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); + } } write_number(n); } @@ -892,7 +922,15 @@ class binary_writer } case value_t::number_float: - return 'D'; + switch (sizeof(typename BasicJsonType::number_float_t)) + { + case 4: // float 32 + return 'd'; + case 8: // float 64 + return 'D'; + default: + JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); + } case value_t::string: return 'S'; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 6b6655af8..deeb9d5de 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -794,6 +794,7 @@ Exceptions have ids 5xx. name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. +json.exception.other_error.502 | type for number_float_t is not supported | The template type for a number is not supported for the binary serialization in CBOR, MessagePack or UBJSON. @sa @ref exception for the base class of the library exceptions @sa @ref parse_error for exceptions indicating a parse error @@ -6357,9 +6358,19 @@ class binary_writer break; } - case value_t::number_float: // Double-Precision Float + case value_t::number_float: { - oa->write_character(static_cast(0xFB)); + switch (sizeof(typename BasicJsonType::number_float_t)) + { + case 4: // Single-Precision Float + oa->write_character(static_cast(0xFA)); + break; + case 8: // Double-Precision Float + oa->write_character(static_cast(0xFB)); + break; + default: + JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); + } write_number(j.m_value.number_float); break; } @@ -6617,9 +6628,19 @@ class binary_writer break; } - case value_t::number_float: // float 64 + case value_t::number_float: { - oa->write_character(static_cast(0xCB)); + switch (sizeof(typename BasicJsonType::number_float_t)) + { + case 4: // float 32 + oa->write_character(static_cast(0xCA)); + break; + case 8: // float 64 + oa->write_character(static_cast(0xCB)); + break; + default: + JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); + } write_number(j.m_value.number_float); break; } @@ -6920,7 +6941,17 @@ class binary_writer { if (add_prefix) { - oa->write_character(static_cast('D')); // float64 + switch (sizeof(typename BasicJsonType::number_float_t)) + { + case 4: // float 32 + oa->write_character(static_cast('d')); + break; + case 8: // float 64 + oa->write_character(static_cast('D')); + break; + default: + JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); + } } write_number(n); } @@ -7100,7 +7131,15 @@ class binary_writer } case value_t::number_float: - return 'D'; + switch (sizeof(typename BasicJsonType::number_float_t)) + { + case 4: // float 32 + return 'd'; + case 8: // float 64 + return 'D'; + default: + JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); + } case value_t::string: return 'S'; diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index c4ca93d91..faa38a140 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -114,6 +114,13 @@ struct nocopy }; } +///////////////////////////////////////////////////////////////////// +// for #1021 +///////////////////////////////////////////////////////////////////// + +using float_json = nlohmann::basic_json; + + TEST_CASE("regression tests") { SECTION("issue #60 - Double quotation mark is not parsed correctly") @@ -1597,4 +1604,15 @@ TEST_CASE("regression tests") auto j = json::parse(geojsonExample, cb, true); CHECK(j == json()); } + + SECTION("issue #1021 - to/from_msgpack only works with standard typization") + { + float_json j = 1000.0; + CHECK(float_json::from_cbor(float_json::to_cbor(j)) == j); + CHECK(float_json::from_msgpack(float_json::to_msgpack(j)) == j); + CHECK(float_json::from_ubjson(float_json::to_ubjson(j)) == j); + + float_json j2 = {1000.0, 2000.0, 3000.0}; + CHECK(float_json::from_ubjson(float_json::to_ubjson(j2, true, true)) == j2); + } } From 896a9db461adbad68d025ef07396905ad6e90fb5 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 28 Mar 2018 19:37:21 +0200 Subject: [PATCH 2/2] :hammer: improved code #1021 --- include/nlohmann/detail/exceptions.hpp | 1 - .../nlohmann/detail/output/binary_writer.hpp | 86 +++++++++--------- single_include/nlohmann/json.hpp | 87 +++++++++---------- test/src/unit-regression.cpp | 2 +- 4 files changed, 79 insertions(+), 97 deletions(-) diff --git a/include/nlohmann/detail/exceptions.hpp b/include/nlohmann/detail/exceptions.hpp index 53b38ec8f..b73d7b1f9 100644 --- a/include/nlohmann/detail/exceptions.hpp +++ b/include/nlohmann/detail/exceptions.hpp @@ -301,7 +301,6 @@ Exceptions have ids 5xx. name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. -json.exception.other_error.502 | type for number_float_t is not supported | The template type for a number is not supported for the binary serialization in CBOR, MessagePack or UBJSON. @sa @ref exception for the base class of the library exceptions @sa @ref parse_error for exceptions indicating a parse error diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 13968fedf..71e5ec81e 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -151,17 +151,7 @@ class binary_writer case value_t::number_float: { - switch (sizeof(typename BasicJsonType::number_float_t)) - { - case 4: // Single-Precision Float - oa->write_character(static_cast(0xFA)); - break; - case 8: // Double-Precision Float - oa->write_character(static_cast(0xFB)); - break; - default: - JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); - } + oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); write_number(j.m_value.number_float); break; } @@ -421,17 +411,7 @@ class binary_writer case value_t::number_float: { - switch (sizeof(typename BasicJsonType::number_float_t)) - { - case 4: // float 32 - oa->write_character(static_cast(0xCA)); - break; - case 8: // float 64 - oa->write_character(static_cast(0xCB)); - break; - default: - JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); - } + oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); write_number(j.m_value.number_float); break; } @@ -608,7 +588,7 @@ class binary_writer if (use_type and not j.m_value.array->empty()) { assert(use_count); - const char first_prefix = ubjson_prefix(j.front()); + const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin() + 1, j.end(), [this, first_prefix](const BasicJsonType & v) { @@ -619,7 +599,7 @@ class binary_writer { prefix_required = false; oa->write_character(static_cast('$')); - oa->write_character(static_cast(first_prefix)); + oa->write_character(first_prefix); } } @@ -653,7 +633,7 @@ class binary_writer if (use_type and not j.m_value.object->empty()) { assert(use_count); - const char first_prefix = ubjson_prefix(j.front()); + const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin(), j.end(), [this, first_prefix](const BasicJsonType & v) { @@ -664,7 +644,7 @@ class binary_writer { prefix_required = false; oa->write_character(static_cast('$')); - oa->write_character(static_cast(first_prefix)); + oa->write_character(first_prefix); } } @@ -732,17 +712,7 @@ class binary_writer { if (add_prefix) { - switch (sizeof(typename BasicJsonType::number_float_t)) - { - case 4: // float 32 - oa->write_character(static_cast('d')); - break; - case 8: // float 64 - oa->write_character(static_cast('D')); - break; - default: - JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); - } + oa->write_character(get_ubjson_float_prefix(n)); } write_number(n); } @@ -863,7 +833,7 @@ class binary_writer write_number_with_ubjson_prefix. Therefore, we return 'L' for any value that does not fit the previous limits. */ - char ubjson_prefix(const BasicJsonType& j) const noexcept + CharType ubjson_prefix(const BasicJsonType& j) const noexcept { switch (j.type()) { @@ -922,15 +892,7 @@ class binary_writer } case value_t::number_float: - switch (sizeof(typename BasicJsonType::number_float_t)) - { - case 4: // float 32 - return 'd'; - case 8: // float 64 - return 'D'; - default: - JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); - } + return get_ubjson_float_prefix(j.m_value.number_float); case value_t::string: return 'S'; @@ -946,6 +908,36 @@ class binary_writer } } + static constexpr CharType get_cbor_float_prefix(float) + { + return static_cast(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double) + { + return static_cast(0xFB); // Double-Precision Float + } + + static constexpr CharType get_msgpack_float_prefix(float) + { + return static_cast(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double) + { + return static_cast(0xCB); // float 64 + } + + static constexpr CharType get_ubjson_float_prefix(float) + { + return 'd'; // float 32 + } + + static constexpr CharType get_ubjson_float_prefix(double) + { + return 'D'; // float 64 + } + private: /// whether we can assume little endianess const bool is_little_endian = binary_reader::little_endianess(); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index deeb9d5de..b7558aa5a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -794,7 +794,6 @@ Exceptions have ids 5xx. name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. -json.exception.other_error.502 | type for number_float_t is not supported | The template type for a number is not supported for the binary serialization in CBOR, MessagePack or UBJSON. @sa @ref exception for the base class of the library exceptions @sa @ref parse_error for exceptions indicating a parse error @@ -6360,17 +6359,7 @@ class binary_writer case value_t::number_float: { - switch (sizeof(typename BasicJsonType::number_float_t)) - { - case 4: // Single-Precision Float - oa->write_character(static_cast(0xFA)); - break; - case 8: // Double-Precision Float - oa->write_character(static_cast(0xFB)); - break; - default: - JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); - } + oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); write_number(j.m_value.number_float); break; } @@ -6630,17 +6619,7 @@ class binary_writer case value_t::number_float: { - switch (sizeof(typename BasicJsonType::number_float_t)) - { - case 4: // float 32 - oa->write_character(static_cast(0xCA)); - break; - case 8: // float 64 - oa->write_character(static_cast(0xCB)); - break; - default: - JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); - } + oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); write_number(j.m_value.number_float); break; } @@ -6817,7 +6796,7 @@ class binary_writer if (use_type and not j.m_value.array->empty()) { assert(use_count); - const char first_prefix = ubjson_prefix(j.front()); + const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin() + 1, j.end(), [this, first_prefix](const BasicJsonType & v) { @@ -6828,7 +6807,7 @@ class binary_writer { prefix_required = false; oa->write_character(static_cast('$')); - oa->write_character(static_cast(first_prefix)); + oa->write_character(first_prefix); } } @@ -6862,7 +6841,7 @@ class binary_writer if (use_type and not j.m_value.object->empty()) { assert(use_count); - const char first_prefix = ubjson_prefix(j.front()); + const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin(), j.end(), [this, first_prefix](const BasicJsonType & v) { @@ -6873,7 +6852,7 @@ class binary_writer { prefix_required = false; oa->write_character(static_cast('$')); - oa->write_character(static_cast(first_prefix)); + oa->write_character(first_prefix); } } @@ -6941,17 +6920,7 @@ class binary_writer { if (add_prefix) { - switch (sizeof(typename BasicJsonType::number_float_t)) - { - case 4: // float 32 - oa->write_character(static_cast('d')); - break; - case 8: // float 64 - oa->write_character(static_cast('D')); - break; - default: - JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); - } + oa->write_character(get_ubjson_float_prefix(n)); } write_number(n); } @@ -7072,7 +7041,7 @@ class binary_writer write_number_with_ubjson_prefix. Therefore, we return 'L' for any value that does not fit the previous limits. */ - char ubjson_prefix(const BasicJsonType& j) const noexcept + CharType ubjson_prefix(const BasicJsonType& j) const noexcept { switch (j.type()) { @@ -7131,15 +7100,7 @@ class binary_writer } case value_t::number_float: - switch (sizeof(typename BasicJsonType::number_float_t)) - { - case 4: // float 32 - return 'd'; - case 8: // float 64 - return 'D'; - default: - JSON_THROW(other_error::create(502, "type for number_float_t is not supported")); - } + return get_ubjson_float_prefix(j.m_value.number_float); case value_t::string: return 'S'; @@ -7155,6 +7116,36 @@ class binary_writer } } + static constexpr CharType get_cbor_float_prefix(float) + { + return static_cast(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double) + { + return static_cast(0xFB); // Double-Precision Float + } + + static constexpr CharType get_msgpack_float_prefix(float) + { + return static_cast(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double) + { + return static_cast(0xCB); // float 64 + } + + static constexpr CharType get_ubjson_float_prefix(float) + { + return 'd'; // float 32 + } + + static constexpr CharType get_ubjson_float_prefix(double) + { + return 'D'; // float 64 + } + private: /// whether we can assume little endianess const bool is_little_endian = binary_reader::little_endianess(); diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index faa38a140..01e689570 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -118,7 +118,7 @@ struct nocopy // for #1021 ///////////////////////////////////////////////////////////////////// -using float_json = nlohmann::basic_json; +using float_json = nlohmann::basic_json; TEST_CASE("regression tests")