From 7427888e05d904593bd8fc7ea7b711d972b86e3b Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sat, 18 May 2019 12:15:36 +0200 Subject: [PATCH] Added `ARDUINOJSON_ENABLE_NAN` to enable NaN in JSON (closes #973) --- CHANGELOG.md | 1 + src/ArduinoJson/Configuration.hpp | 5 ++++ src/ArduinoJson/Json/TextFormatter.hpp | 2 +- src/ArduinoJson/Namespace.hpp | 7 ++++-- src/ArduinoJson/Numbers/parseNumber.hpp | 3 ++- test/MixedConfiguration/CMakeLists.txt | 2 ++ test/MixedConfiguration/enable_nan_0.cpp | 25 +++++++++++++++++++ test/MixedConfiguration/enable_nan_1.cpp | 31 ++++++++++++++++++++++++ 8 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 test/MixedConfiguration/enable_nan_0.cpp create mode 100644 test/MixedConfiguration/enable_nan_1.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 116e070b..7e21e643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ HEAD * Fixed `deserializeJson()` silently accepting a `Stream*` (issue #978) * Fixed invalid result from `operator|` (issue #981) * Made `deserializeJson()` more picky about trailing characters (issue #980) +* Added `ARDUINOJSON_ENABLE_NAN` to enable NaN in JSON (issue #973) > ### BREAKING CHANGE > diff --git a/src/ArduinoJson/Configuration.hpp b/src/ArduinoJson/Configuration.hpp index 662c4d7f..5fd66d25 100644 --- a/src/ArduinoJson/Configuration.hpp +++ b/src/ArduinoJson/Configuration.hpp @@ -135,6 +135,11 @@ #define ARDUINOJSON_DECODE_UNICODE 0 #endif +// Support NaN in JSON +#ifndef ARDUINOJSON_ENABLE_NAN +#define ARDUINOJSON_ENABLE_NAN 1 +#endif + // Control the exponentiation threshold for big numbers // CAUTION: cannot be more that 1e9 !!!! #ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD diff --git a/src/ArduinoJson/Json/TextFormatter.hpp b/src/ArduinoJson/Json/TextFormatter.hpp index 70c0bd39..29bc06ad 100644 --- a/src/ArduinoJson/Json/TextFormatter.hpp +++ b/src/ArduinoJson/Json/TextFormatter.hpp @@ -52,7 +52,7 @@ class TextFormatter { template void writeFloat(T value) { - if (isnan(value)) return writeRaw("NaN"); + if (isnan(value)) return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null"); if (value < 0.0) { writeRaw('-'); diff --git a/src/ArduinoJson/Namespace.hpp b/src/ArduinoJson/Namespace.hpp index aef7cba2..dee7d36f 100644 --- a/src/ArduinoJson/Namespace.hpp +++ b/src/ArduinoJson/Namespace.hpp @@ -15,9 +15,12 @@ #define ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, H) \ ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT4(A, B, C, D), \ ARDUINOJSON_CONCAT4(E, F, G, H)) +#define ARDUINOJSON_CONCAT9(A, B, C, D, E, F, G, H, I) \ + ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT4(A, B, C, D), \ + ARDUINOJSON_CONCAT4(E, F, G, ARDUINOJSON_CONCAT2(H, I))) #define ARDUINOJSON_NAMESPACE \ - ARDUINOJSON_CONCAT8(ArduinoJson, ARDUINOJSON_VERSION_MAJOR, \ + ARDUINOJSON_CONCAT9(ArduinoJson, ARDUINOJSON_VERSION_MAJOR, \ ARDUINOJSON_VERSION_MINOR, ARDUINOJSON_VERSION_REVISION, \ _, ARDUINOJSON_USE_LONG_LONG, ARDUINOJSON_USE_DOUBLE, \ - ARDUINOJSON_DECODE_UNICODE) + ARDUINOJSON_DECODE_UNICODE, ARDUINOJSON_ENABLE_NAN) diff --git a/src/ArduinoJson/Numbers/parseNumber.hpp b/src/ArduinoJson/Numbers/parseNumber.hpp index 6db0d1bd..a99efb7a 100644 --- a/src/ArduinoJson/Numbers/parseNumber.hpp +++ b/src/ArduinoJson/Numbers/parseNumber.hpp @@ -71,8 +71,9 @@ inline ParsedNumber parseNumber(const char *s) { s++; break; } - +#if ARDUINOJSON_ENABLE_NAN if (*s == 'n' || *s == 'N') return traits::nan(); +#endif if (*s == 'i' || *s == 'I') return is_negative ? -traits::inf() : traits::inf(); if (!isdigit(*s) && *s != '.') return return_type(); diff --git a/test/MixedConfiguration/CMakeLists.txt b/test/MixedConfiguration/CMakeLists.txt index cb99c23a..08a7b2e8 100644 --- a/test/MixedConfiguration/CMakeLists.txt +++ b/test/MixedConfiguration/CMakeLists.txt @@ -12,6 +12,8 @@ add_executable(MixedConfigurationTests use_double_1.cpp use_long_long_0.cpp use_long_long_1.cpp + enable_nan_0.cpp + enable_nan_1.cpp ) target_link_libraries(MixedConfigurationTests catch) diff --git a/test/MixedConfiguration/enable_nan_0.cpp b/test/MixedConfiguration/enable_nan_0.cpp new file mode 100644 index 00000000..88425253 --- /dev/null +++ b/test/MixedConfiguration/enable_nan_0.cpp @@ -0,0 +1,25 @@ +#define ARDUINOJSON_ENABLE_NAN 0 +#include + +#include +#include + +TEST_CASE("ARDUINOJSON_ENABLE_NAN == 0") { + DynamicJsonDocument doc(4096); + JsonObject root = doc.to(); + + SECTION("serializeJson()") { + root["X"] = std::numeric_limits::signaling_NaN(); + + std::string json; + serializeJson(doc, json); + + REQUIRE(json == "{\"X\":null}"); + } + + SECTION("deserializeJson()") { + auto err = deserializeJson(doc, "{\"X\":NaN}"); + + REQUIRE(err == DeserializationError::InvalidInput); + } +} diff --git a/test/MixedConfiguration/enable_nan_1.cpp b/test/MixedConfiguration/enable_nan_1.cpp new file mode 100644 index 00000000..19e50d82 --- /dev/null +++ b/test/MixedConfiguration/enable_nan_1.cpp @@ -0,0 +1,31 @@ +#define ARDUINOJSON_ENABLE_NAN 1 +#include + +#include +#include + +namespace my { +using ARDUINOJSON_NAMESPACE::isnan; +} // namespace my + +TEST_CASE("ARDUINOJSON_ENABLE_NAN == 1") { + DynamicJsonDocument doc(4096); + JsonObject root = doc.to(); + + SECTION("serializeJson()") { + root["X"] = std::numeric_limits::signaling_NaN(); + + std::string json; + serializeJson(doc, json); + + REQUIRE(json == "{\"X\":NaN}"); + } + + SECTION("deserializeJson()") { + auto err = deserializeJson(doc, "{\"X\":NaN}"); + float x = doc["X"]; + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(my::isnan(x)); + } +}