From d3a1203782856bf8907961e6b0212db60d40364d Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 29 Nov 2017 09:49:40 +0100 Subject: [PATCH] Added JsonVariant::operator| to return a default value --- CHANGELOG.md | 263 ++++++++++------------------ src/ArduinoJson/JsonVariantBase.hpp | 2 + src/ArduinoJson/JsonVariantOr.hpp | 36 ++++ test/JsonVariant/CMakeLists.txt | 1 + test/JsonVariant/or.cpp | 77 ++++++++ 5 files changed, 206 insertions(+), 173 deletions(-) create mode 100644 src/ArduinoJson/JsonVariantOr.hpp create mode 100644 test/JsonVariant/or.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b6ac9f..e3f837a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,26 @@ ArduinoJson: change log HEAD ---- + +* Added `JsonVariant::operator|` to return a default value (see bellow) * Rewrote example `JsonHttpClient.ino` (issue #600) +> ### How to use the new feature? +> +> If you have a block like this: +> +> ```c++ +> const char* ssid = root["ssid"]; +> if (!ssid) +> ssid = "default ssid"; +> ``` +> +> You can simplify like that: +> +> ```c++ +> const char* ssid = root["ssid"] | "default ssid"; +> ``` + v5.11.2 ------- @@ -46,27 +64,26 @@ v5.10.0 * Fixed error `IsBaseOf is not a member of ArduinoJson::TypeTraits` (issue #495) * Fixed error `forming reference to reference` (issue #495) -### BREAKING CHANGES :warning: - -| Old syntax | New syntax | -|:--------------------------------|:--------------------| -| `double_with_n_digits(3.14, 2)` | `3.14` | -| `float_with_n_digits(3.14, 2)` | `3.14f` | -| `obj.set("key", 3.14, 2)` | `obj["key"] = 3.14` | -| `arr.add(3.14, 2)` | `arr.add(3.14)` | - -| Input | Old output | New output | -|:----------|:-----------|:-----------| -| `3.14159` | `3.14` | `3.14159` | -| `42.0` | `42.00` | `42` | -| `0.0` | `0.00` | `0` | - -| Expression | Old result | New result | -|:-------------------------------|:-----------|:-----------| -| `JsonVariant(42).is()` | `true` | `true` | -| `JsonVariant(42).is()` | `false` | `true` | -| `JsonVariant(42).is()` | `false` | `true` | - +> ### BREAKING CHANGES :warning: +> +> | Old syntax | New syntax | +> |:--------------------------------|:--------------------| +> | `double_with_n_digits(3.14, 2)` | `3.14` | +> | `float_with_n_digits(3.14, 2)` | `3.14f` | +> | `obj.set("key", 3.14, 2)` | `obj["key"] = 3.14` | +> | `arr.add(3.14, 2)` | `arr.add(3.14)` | +> +> | Input | Old output | New output | +> |:----------|:-----------|:-----------| +> | `3.14159` | `3.14` | `3.14159` | +> | `42.0` | `42.00` | `42` | +> | `0.0` | `0.00` | `0` | +> +> | Expression | Old result | New result | +> |:-------------------------------|:-----------|:-----------| +> | `JsonVariant(42).is()` | `true` | `true` | +> | `JsonVariant(42).is()` | `false` | `true` | +> | `JsonVariant(42).is()` | `false` | `true` | v5.9.0 ------ @@ -120,24 +137,23 @@ v5.8.0 * Added support for `Stream` (issue #300) * Reduced memory consumption by not duplicating spaces and comments -### BREAKING CHANGES :warning: - -`JsonBuffer::parseObject()` and `JsonBuffer::parseArray()` have been pulled down to the derived classes `DynamicJsonBuffer` and `StaticJsonBufferBase`. - -This means that if you have code like: - -```c++ -void myFunction(JsonBuffer& jsonBuffer); -``` - -you need to replace it with one of the following: - -```c++ -void myFunction(DynamicJsonBuffer& jsonBuffer); -void myFunction(StaticJsonBufferBase& jsonBuffer); -template void myFunction(TJsonBuffer& jsonBuffer); -``` - +> ### BREAKING CHANGES :warning: +> +> `JsonBuffer::parseObject()` and `JsonBuffer::parseArray()` have been pulled down to the derived classes `DynamicJsonBuffer` and `StaticJsonBufferBase`. +> +> This means that if you have code like: +> +> ```c++ +> void myFunction(JsonBuffer& jsonBuffer); +> ``` +> +> you need to replace it with one of the following: +> +> ```c++ +> void myFunction(DynamicJsonBuffer& jsonBuffer); +> void myFunction(StaticJsonBufferBase& jsonBuffer); +> template void myFunction(TJsonBuffer& jsonBuffer); +> ``` v5.7.3 ------ @@ -170,27 +186,26 @@ v5.7.0 * Added example `StringExample.ino` to show where `String` can be used * Increased default nesting limit to 50 when compiled for a computer (issue #349) -### BREAKING CHANGES :warning: - -The non-template functions `JsonObject::get()` and `JsonArray.get()` have been removed. This means that you need to explicitely tell the type you expect in return. - -Old code: - -```c++ -#define ARDUINOJSON_USE_ARDUINO_STRING 0 -JsonVariant value1 = myObject.get("myKey"); -JsonVariant value2 = myArray.get(0); -``` - -New code: - -```c++ -#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 -#define ARDUINOJSON_ENABLE_STD_STRING 1 -JsonVariant value1 = myObject.get("myKey"); -JsonVariant value2 = myArray.get(0); -``` - +> ### BREAKING CHANGES :warning: +> +> The non-template functions `JsonObject::get()` and `JsonArray.get()` have been removed. This means that you need to explicitely tell the type you expect in return. +> +> Old code: +> +> ```c++ +> #define ARDUINOJSON_USE_ARDUINO_STRING 0 +> JsonVariant value1 = myObject.get("myKey"); +> JsonVariant value2 = myArray.get(0); +> ``` +> +> New code: +> +> ```c++ +> #define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 +> #define ARDUINOJSON_ENABLE_STD_STRING 1 +> JsonVariant value1 = myObject.get("myKey"); +> JsonVariant value2 = myArray.get(0); +> ``` v5.6.7 ------ @@ -282,8 +297,9 @@ v5.1.0 * Added support of `long long` (issue #171) * Moved all build settings to `ArduinoJson/Configuration.hpp` -**BREAKING CHANGE**: -If you defined `ARDUINOJSON_ENABLE_STD_STREAM`, you now need to define it to `1`. +> ### BREAKING CHANGE :warning: +> +> If you defined `ARDUINOJSON_ENABLE_STD_STREAM`, you now need to define it to `1`. v5.0.8 ------ @@ -297,10 +313,10 @@ v5.0.7 * Made library easier to use from a CMake project: simply `add_subdirectory(ArduinoJson/src)` * Changed `String` to be a `typedef` of `std::string` (issues #142 and #161) -### BREAKING CHANGES :warning: - -- `JsonVariant(true).as()` now returns `"true"` instead of `"1"` -- `JsonVariant(false).as()` now returns `"false"` instead of `"0"` +> ### BREAKING CHANGES :warning: +> +> - `JsonVariant(true).as()` now returns `"true"` instead of `"1"` +> - `JsonVariant(false).as()` now returns `"false"` instead of `"0"` v5.0.6 ------ @@ -354,11 +370,11 @@ v5.0.0 * Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators (issue #66) * Switched to new the library layout (requires Arduino 1.0.6 or above) -### BREAKING CHANGES :warning: - -- `JsonObject::add()` was renamed to `set()` -- `JsonArray::at()` and `JsonObject::at()` were renamed to `get()` -- Number of digits of floating point value are now set with `double_with_n_digits()` +> ### BREAKING CHANGES :warning: +> +> - `JsonObject::add()` was renamed to `set()` +> - `JsonArray::at()` and `JsonObject::at()` were renamed to `get()` +> - Number of digits of floating point value are now set with `double_with_n_digits()` **Personal note about the `String` class**: Support of the `String` class has been added to the library because many people use it in their programs. @@ -411,106 +427,7 @@ v4.0 * Unified parser and generator API (issue #23) * Updated library layout, now requires Arduino 1.0.6 or newer -**BREAKING CHANGE**: API changed significantly, see [Migrating code to the new API](https://github.com/bblanchon/ArduinoJson/wiki/Migrating-code-to-the-new-API). +> ### BREAKING CHANGES :warning: +> +> API changed significantly since v3, see [Migrating code to the new API](http://arduinojson.org/doc/migration/). - -v3.4 ----- - -* Fixed escaped char parsing (issue #16) - - -v3.3 ----- - -* Added indented output for the JSON generator (issue #11), see example bellow. -* Added `IndentedPrint`, a decorator for `Print` to allow indented output - -Example: - - JsonOject<2> json; - json["key"] = "value"; - json.prettyPrintTo(Serial); - -v3.2 ----- - -* Fixed a bug when adding nested object in `JsonArray` (bug introduced in v3.1). - -v3.1 ----- - -* Calling `Generator::JsonObject::add()` twice with the same `key` now replaces the `value` -* Added `Generator::JsonObject::operator[]`, see bellow the new API -* Added `Generator::JsonObject::remove()` (issue #9) - -Old generator API: - - JsonObject<3> root; - root.add("sensor", "gps"); - root.add("time", 1351824120); - root.add("data", array); - -New generator API: - - JsonObject<3> root; - root["sensor"] = "gps"; - root["time"] = 1351824120; - root["data"] = array; - -v3.0 ----- - -* New parser API, see bellow -* Renamed `JsonHashTable` into `JsonObject` -* Added iterators for `JsonArray` and `JsonObject` (issue #4) - -Old parser API: - - JsonHashTable root = parser.parseHashTable(json); - - char* sensor = root.getString("sensor"); - long time = root.getLong("time"); - double latitude = root.getArray("data").getDouble(0); - double longitude = root.getArray("data").getDouble(1); - -New parser API: - - JsonObject root = parser.parse(json); - - char* sensor = root["sensor"]; - long time = root["time"]; - double latitude = root["data"][0]; - double longitude = root["data"][1]; - -v2.1 ----- - -* Fixed case `#include "jsmn.cpp"` which caused an error in Linux (issue #6) -* Fixed a buffer overrun in JSON Parser (issue #5) - -v2.0 ----- - -* Added JSON encoding (issue #2) -* Renamed the library `ArduinoJsonParser` becomes `ArduinoJson` - -**Breaking change**: you need to add the following line at the top of your program. - - using namespace ArduinoJson::Parser; - -v1.2 ----- - -* Fixed error in JSON parser example (issue #1) - -v1.1 ----- - -* Example: changed `char* json` into `char[] json` so that the bytes are not write protected -* Fixed parsing bug when the JSON contains multi-dimensional arrays - -v1.0 ----- - -Initial release diff --git a/src/ArduinoJson/JsonVariantBase.hpp b/src/ArduinoJson/JsonVariantBase.hpp index 2e2a67e1..46f18a6f 100644 --- a/src/ArduinoJson/JsonVariantBase.hpp +++ b/src/ArduinoJson/JsonVariantBase.hpp @@ -6,6 +6,7 @@ #include "JsonVariantCasts.hpp" #include "JsonVariantComparisons.hpp" +#include "JsonVariantOr.hpp" #include "JsonVariantSubscripts.hpp" #include "Serialization/JsonPrintable.hpp" @@ -15,6 +16,7 @@ template class JsonVariantBase : public Internals::JsonPrintable, public JsonVariantCasts, public JsonVariantComparisons, + public JsonVariantOr, public JsonVariantSubscripts, public TypeTraits::JsonVariantTag {}; } diff --git a/src/ArduinoJson/JsonVariantOr.hpp b/src/ArduinoJson/JsonVariantOr.hpp new file mode 100644 index 00000000..564f1dc3 --- /dev/null +++ b/src/ArduinoJson/JsonVariantOr.hpp @@ -0,0 +1,36 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2017 +// MIT License + +#pragma once + +#include "Data/JsonVariantAs.hpp" +#include "Polyfills/attributes.hpp" + +namespace ArduinoJson { + +template +class JsonVariantOr { + public: + // Returns the default value if the JsonVariant is undefined of incompatible + template + T operator|(const T &defaultValue) const { + if (impl()->template is()) + return impl()->template as(); + else + return defaultValue; + } + + // Returns the default value if the JsonVariant is undefined of incompatible + // Special case for string: null is treated as undefined + const char *operator|(const char *defaultValue) const { + const char *value = impl()->template as(); + return value ? value : defaultValue; + } + + private: + const TImpl *impl() const { + return static_cast(this); + } +}; +} diff --git a/test/JsonVariant/CMakeLists.txt b/test/JsonVariant/CMakeLists.txt index 592dcbd8..02bff3df 100644 --- a/test/JsonVariant/CMakeLists.txt +++ b/test/JsonVariant/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(JsonVariantTests compare.cpp copy.cpp is.cpp + or.cpp printTo.cpp set_get.cpp subscript.cpp diff --git a/test/JsonVariant/or.cpp b/test/JsonVariant/or.cpp new file mode 100644 index 00000000..98a7f611 --- /dev/null +++ b/test/JsonVariant/or.cpp @@ -0,0 +1,77 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2017 +// MIT License + +#include +#include + +static const JsonVariant undefined; +static const JsonVariant null = static_cast(0); + +TEST_CASE("JsonVariant::operator|()") { + SECTION("undefined | const char*") { + std::string result = undefined | "default"; + REQUIRE(result == "default"); + } + + SECTION("undefined | int") { + int result = undefined | 42; + REQUIRE(result == 42); + } + + SECTION("undefined | bool") { + bool result = undefined | true; + REQUIRE(result == true); + } + + SECTION("null | const char*") { + std::string result = null | "default"; + REQUIRE(result == "default"); + } + + SECTION("null | int") { + int result = null | 42; + REQUIRE(result == 42); + } + + SECTION("null | bool") { + bool result = null | true; + REQUIRE(result == true); + } + + SECTION("int | const char*") { + JsonVariant variant = 42; + std::string result = variant | "default"; + REQUIRE(result == "default"); + } + + SECTION("int | int") { + JsonVariant variant = 0; + int result = variant | 666; + REQUIRE(result == 0); + } + + SECTION("bool | bool") { + JsonVariant variant = false; + bool result = variant | true; + REQUIRE(result == false); + } + + SECTION("int | bool") { + JsonVariant variant = 0; + bool result = variant | true; + REQUIRE(result == true); + } + + SECTION("const char* | const char*") { + JsonVariant variant = "not default"; + std::string result = variant | "default"; + REQUIRE(result == "not default"); + } + + SECTION("const char* | int") { + JsonVariant variant = "not default"; + int result = variant | 42; + REQUIRE(result == 42); + } +}