Skip to content

Commit d7f5b56

Browse files
committed
Added support for custom converters (closes #687)
1 parent 53d6f0d commit d7f5b56

29 files changed

+552
-468
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ ArduinoJson: change log
44
HEAD
55
----
66

7+
* Added support for custom converters (issue #687)
78
* Removed support for `char` values, see below (issue #1498)
89
* `deserializeJson()` leaves `\uXXXX` unchanged instead of returning `NotSupported`
910
* `deserializeMsgPack()` inserts `null` instead of returning `NotSupported`

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
3838
* Supports Arduino's `Stream` and [STL's `std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/?utm_source=github&utm_medium=readme)
3939
* [Supports Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/?utm_source=github&utm_medium=readme)
4040
* Supports [custom readers](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme#custom-reader) and [custom writers](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme#custom-writer)
41+
* Supports custom converters
4142
* Portable
4243
* Usable on any C++ project (not limited to Arduino)
4344
* Compatible with C++98

extras/tests/JsonVariant/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_executable(JsonVariantTests
99
compare.cpp
1010
containsKey.cpp
1111
copy.cpp
12+
converters.cpp
1213
createNested.cpp
1314
is.cpp
1415
isnull.cpp
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// ArduinoJson - arduinojson.org
2+
// Copyright Benoit Blanchon 2014-2021
3+
// MIT License
4+
5+
#include <ArduinoJson.h>
6+
#include <stdint.h>
7+
#include <catch.hpp>
8+
9+
namespace {
10+
struct Date {
11+
int day;
12+
int month;
13+
int year;
14+
};
15+
16+
bool convertToJson(JsonVariant variant, const Date& date) {
17+
variant["day"] = date.day;
18+
variant["month"] = date.month;
19+
variant["year"] = date.year;
20+
return true;
21+
}
22+
23+
void convertFromJson(Date& date, JsonVariantConst variant) {
24+
date.day = variant["day"];
25+
date.month = variant["month"];
26+
date.year = variant["year"];
27+
}
28+
29+
bool canConvertFromJson(Date&, JsonVariantConst variant) {
30+
return variant["day"].is<int>() && variant["month"].is<int>() &&
31+
variant["year"].is<int>();
32+
}
33+
} // namespace
34+
35+
TEST_CASE("Custom converter with overloading") {
36+
DynamicJsonDocument doc(4096);
37+
38+
SECTION("convert JSON to Date") {
39+
doc["date"]["day"] = 2;
40+
doc["date"]["month"] = 3;
41+
doc["date"]["year"] = 2021;
42+
43+
Date date = doc["date"];
44+
45+
REQUIRE(date.day == 2);
46+
REQUIRE(date.month == 3);
47+
REQUIRE(date.year == 2021);
48+
}
49+
50+
SECTION("is<Date>() returns true") {
51+
doc["date"]["day"] = 2;
52+
doc["date"]["month"] = 3;
53+
doc["date"]["year"] = 2021;
54+
55+
REQUIRE(doc["date"].is<Date>());
56+
}
57+
58+
SECTION("is<Date>() returns false") {
59+
doc["date"]["day"] = 2;
60+
doc["date"]["month"] = 3;
61+
doc["date"]["year"] = "2021";
62+
63+
REQUIRE(doc["date"].is<Date>() == false);
64+
}
65+
66+
SECTION("convert Date to JSON") {
67+
Date date = {19, 3, 2021};
68+
doc["date"] = date;
69+
70+
REQUIRE(doc["date"]["day"] == 19);
71+
REQUIRE(doc["date"]["month"] == 3);
72+
REQUIRE(doc["date"]["year"] == 2021);
73+
}
74+
}
75+
76+
class Complex {
77+
public:
78+
explicit Complex(double r, double i) : _real(r), _imag(i) {}
79+
80+
double real() const {
81+
return _real;
82+
}
83+
84+
double imag() const {
85+
return _imag;
86+
}
87+
88+
private:
89+
double _real, _imag;
90+
};
91+
92+
namespace ARDUINOJSON_NAMESPACE {
93+
template <>
94+
struct Converter<Complex> {
95+
static bool toJson(VariantRef variant, const Complex& value) {
96+
variant["real"] = value.real();
97+
variant["imag"] = value.imag();
98+
return true;
99+
}
100+
101+
static Complex fromJson(VariantConstRef variant) {
102+
return Complex(variant["real"], variant["imag"]);
103+
}
104+
105+
static bool checkJson(VariantConstRef variant) {
106+
return variant["real"].is<double>() && variant["imag"].is<double>();
107+
}
108+
};
109+
} // namespace ARDUINOJSON_NAMESPACE
110+
111+
TEST_CASE("Custom converter with specialization") {
112+
DynamicJsonDocument doc(4096);
113+
114+
SECTION("convert JSON to Complex") {
115+
doc["value"]["real"] = 2;
116+
doc["value"]["imag"] = 3;
117+
118+
Complex value = doc["value"];
119+
120+
REQUIRE(value.real() == 2);
121+
REQUIRE(value.imag() == 3);
122+
}
123+
124+
SECTION("is<Complex>() returns true") {
125+
doc["value"]["real"] = 2;
126+
doc["value"]["imag"] = 3;
127+
128+
REQUIRE(doc["value"].is<Complex>());
129+
}
130+
131+
SECTION("is<Complex>() returns false") {
132+
doc["value"]["real"] = 2;
133+
doc["value"]["imag"] = "3";
134+
135+
REQUIRE(doc["value"].is<Complex>() == false);
136+
}
137+
138+
SECTION("convert value to JSON") {
139+
doc["value"] = Complex(19, 3);
140+
141+
REQUIRE(doc["value"]["real"] == 19);
142+
REQUIRE(doc["value"]["imag"] == 3);
143+
}
144+
}

extras/tests/JsonVariant/is.cpp

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ TEST_CASE("JsonVariant::is<T>()") {
1919
CHECK(variant.is<JsonVariant>() == false);
2020
CHECK(variant.is<JsonVariantConst>() == false);
2121
CHECK(variant.is<bool>() == false);
22-
CHECK(variant.is<char *>() == false);
22+
CHECK(variant.is<const char *>() == false);
2323
CHECK(variant.is<int>() == false);
2424
CHECK(variant.is<std::string>() == false);
2525
CHECK(variant.is<float>() == false);
@@ -32,7 +32,7 @@ TEST_CASE("JsonVariant::is<T>()") {
3232
CHECK(variant.is<JsonObject>() == false);
3333
CHECK(variant.is<JsonArray>() == false);
3434
CHECK(variant.is<bool>() == false);
35-
CHECK(variant.is<char *>() == false);
35+
CHECK(variant.is<const char *>() == false);
3636
CHECK(variant.is<int>() == false);
3737
CHECK(variant.is<std::string>() == false);
3838
CHECK(variant.is<float>() == false);
@@ -47,7 +47,7 @@ TEST_CASE("JsonVariant::is<T>()") {
4747
CHECK(variant.is<JsonVariantConst>() == true);
4848
CHECK(variant.is<JsonObject>() == false);
4949
CHECK(variant.is<JsonArray>() == false);
50-
CHECK(variant.is<char *>() == false);
50+
CHECK(variant.is<const char *>() == false);
5151
CHECK(variant.is<int>() == false);
5252
CHECK(variant.is<std::string>() == false);
5353
CHECK(variant.is<float>() == false);
@@ -62,7 +62,7 @@ TEST_CASE("JsonVariant::is<T>()") {
6262
CHECK(variant.is<JsonVariantConst>() == true);
6363
CHECK(variant.is<JsonObject>() == false);
6464
CHECK(variant.is<JsonArray>() == false);
65-
CHECK(variant.is<char *>() == false);
65+
CHECK(variant.is<const char *>() == false);
6666
CHECK(variant.is<int>() == false);
6767
CHECK(variant.is<std::string>() == false);
6868
CHECK(variant.is<float>() == false);
@@ -83,7 +83,7 @@ TEST_CASE("JsonVariant::is<T>()") {
8383
CHECK(variant.is<bool>() == false);
8484
CHECK(variant.is<JsonObject>() == false);
8585
CHECK(variant.is<JsonArray>() == false);
86-
CHECK(variant.is<char *>() == false);
86+
CHECK(variant.is<const char *>() == false);
8787
CHECK(variant.is<std::string>() == false);
8888
}
8989

@@ -97,7 +97,7 @@ TEST_CASE("JsonVariant::is<T>()") {
9797
CHECK(variant.is<bool>() == false);
9898
CHECK(variant.is<JsonObject>() == false);
9999
CHECK(variant.is<JsonArray>() == false);
100-
CHECK(variant.is<char *>() == false);
100+
CHECK(variant.is<const char *>() == false);
101101
CHECK(variant.is<int>() == false);
102102
CHECK(variant.is<std::string>() == false);
103103
CHECK(variant.is<MYENUM2>() == false);
@@ -106,7 +106,7 @@ TEST_CASE("JsonVariant::is<T>()") {
106106
SECTION("const char*") {
107107
variant.set("4.2");
108108

109-
CHECK(variant.is<char *>() == true);
109+
CHECK(variant.is<const char *>() == true);
110110
CHECK(variant.is<const char *>() == true);
111111
CHECK(variant.is<std::string>() == true);
112112
CHECK(variant.is<JsonVariant>() == true);
@@ -132,7 +132,7 @@ TEST_CASE("JsonVariant::is<T>()") {
132132
CHECK(variant.is<int>() == false);
133133
CHECK(variant.is<float>() == false);
134134
CHECK(variant.is<bool>() == false);
135-
CHECK(variant.is<char *>() == false);
135+
CHECK(variant.is<const char *>() == false);
136136
CHECK(variant.is<MYENUM2>() == false);
137137
}
138138

@@ -148,7 +148,7 @@ TEST_CASE("JsonVariant::is<T>()") {
148148
CHECK(variant.is<int>() == false);
149149
CHECK(variant.is<float>() == false);
150150
CHECK(variant.is<bool>() == false);
151-
CHECK(variant.is<char *>() == false);
151+
CHECK(variant.is<const char *>() == false);
152152
CHECK(variant.is<MYENUM2>() == false);
153153
CHECK(variant.is<JsonVariant>() == true);
154154
CHECK(variant.is<JsonVariantConst>() == true);
@@ -170,7 +170,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
170170
CHECK(cvariant.is<JsonVariant>() == false);
171171
CHECK(cvariant.is<JsonVariantConst>() == false);
172172
CHECK(cvariant.is<bool>() == false);
173-
CHECK(cvariant.is<char *>() == false);
173+
CHECK(cvariant.is<const char *>() == false);
174174
CHECK(cvariant.is<int>() == false);
175175
CHECK(cvariant.is<std::string>() == false);
176176
CHECK(cvariant.is<float>() == false);
@@ -183,7 +183,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
183183
CHECK(cvariant.is<JsonArray>() == false);
184184
CHECK(cvariant.is<JsonVariant>() == false);
185185
CHECK(cvariant.is<bool>() == false);
186-
CHECK(cvariant.is<char *>() == false);
186+
CHECK(cvariant.is<const char *>() == false);
187187
CHECK(cvariant.is<int>() == false);
188188
CHECK(cvariant.is<std::string>() == false);
189189
CHECK(cvariant.is<float>() == false);
@@ -198,7 +198,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
198198
CHECK(cvariant.is<JsonVariant>() == false);
199199
CHECK(cvariant.is<JsonObject>() == false);
200200
CHECK(cvariant.is<JsonArray>() == false);
201-
CHECK(cvariant.is<char *>() == false);
201+
CHECK(cvariant.is<const char *>() == false);
202202
CHECK(cvariant.is<int>() == false);
203203
CHECK(cvariant.is<std::string>() == false);
204204
CHECK(cvariant.is<float>() == false);
@@ -213,7 +213,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
213213
CHECK(cvariant.is<JsonVariant>() == false);
214214
CHECK(cvariant.is<JsonObject>() == false);
215215
CHECK(cvariant.is<JsonArray>() == false);
216-
CHECK(cvariant.is<char *>() == false);
216+
CHECK(cvariant.is<const char *>() == false);
217217
CHECK(cvariant.is<int>() == false);
218218
CHECK(cvariant.is<std::string>() == false);
219219
CHECK(cvariant.is<float>() == false);
@@ -234,7 +234,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
234234
CHECK(cvariant.is<JsonObject>() == false);
235235
CHECK(cvariant.is<JsonArray>() == false);
236236
CHECK(cvariant.is<JsonVariant>() == false);
237-
CHECK(cvariant.is<char *>() == false);
237+
CHECK(cvariant.is<const char *>() == false);
238238
CHECK(cvariant.is<std::string>() == false);
239239
}
240240

@@ -248,7 +248,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
248248
CHECK(cvariant.is<JsonObject>() == false);
249249
CHECK(cvariant.is<JsonArray>() == false);
250250
CHECK(cvariant.is<JsonVariant>() == false);
251-
CHECK(cvariant.is<char *>() == false);
251+
CHECK(cvariant.is<const char *>() == false);
252252
CHECK(cvariant.is<int>() == false);
253253
CHECK(cvariant.is<std::string>() == false);
254254
CHECK(cvariant.is<MYENUM2>() == false);
@@ -257,7 +257,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
257257
SECTION("const char*") {
258258
variant.set("4.2");
259259

260-
CHECK(cvariant.is<char *>() == true);
260+
CHECK(cvariant.is<const char *>() == true);
261261
CHECK(cvariant.is<const char *>() == true);
262262
CHECK(cvariant.is<std::string>() == true);
263263
CHECK(cvariant.is<double>() == false);
@@ -282,7 +282,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
282282
CHECK(cvariant.is<int>() == false);
283283
CHECK(cvariant.is<float>() == false);
284284
CHECK(cvariant.is<bool>() == false);
285-
CHECK(cvariant.is<char *>() == false);
285+
CHECK(cvariant.is<const char *>() == false);
286286
CHECK(cvariant.is<MYENUM2>() == false);
287287
}
288288

@@ -298,7 +298,7 @@ TEST_CASE("JsonVariantConst::is<T>()") {
298298
CHECK(cvariant.is<int>() == false);
299299
CHECK(cvariant.is<float>() == false);
300300
CHECK(cvariant.is<bool>() == false);
301-
CHECK(cvariant.is<char *>() == false);
301+
CHECK(cvariant.is<const char *>() == false);
302302
CHECK(cvariant.is<MYENUM2>() == false);
303303
}
304304
}

extras/tests/JsonVariant/undefined.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ TEST_CASE("JsonVariant undefined") {
4747
REQUIRE(variant.is<unsigned>() == false);
4848
}
4949

50-
SECTION("char*") {
51-
REQUIRE(variant.is<char*>() == false);
50+
SECTION("const char*") {
51+
REQUIRE(variant.is<const char*>() == false);
5252
}
5353

5454
SECTION("double") {

extras/tests/Misc/Readers.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// MIT License
44

55
#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1
6-
#include <ArduinoJson/Deserialization/Reader.hpp>
6+
#include <ArduinoJson.hpp>
77
#include <catch.hpp>
88

99
using namespace ARDUINOJSON_NAMESPACE;

extras/tests/Numbers/parseDouble.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
#define ARDUINOJSON_ENABLE_NAN 1
77
#define ARDUINOJSON_ENABLE_INFINITY 1
88

9-
#include <ArduinoJson/Numbers/parseNumber.hpp>
10-
#include <ArduinoJson/Variant/VariantImpl.hpp>
9+
#include <ArduinoJson.hpp>
1110
#include <catch.hpp>
1211

1312
using namespace ARDUINOJSON_NAMESPACE;

extras/tests/Numbers/parseFloat.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
#define ARDUINOJSON_ENABLE_NAN 1
77
#define ARDUINOJSON_ENABLE_INFINITY 1
88

9-
#include <ArduinoJson/Numbers/parseNumber.hpp>
10-
#include <ArduinoJson/Variant/VariantImpl.hpp>
9+
#include <ArduinoJson.hpp>
1110
#include <catch.hpp>
1211

1312
using namespace ARDUINOJSON_NAMESPACE;

extras/tests/Numbers/parseInteger.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
// MIT License
44

55
#include <stdint.h>
6-
#include <ArduinoJson/Numbers/parseNumber.hpp>
7-
#include <ArduinoJson/Variant/VariantImpl.hpp>
6+
#include <ArduinoJson.hpp>
87
#include <catch.hpp>
98

109
using namespace ARDUINOJSON_NAMESPACE;

extras/tests/Numbers/parseNumber.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
// Copyright Benoit Blanchon 2014-2021
33
// MIT License
44

5-
#include <ArduinoJson/Numbers/Integer.hpp>
6-
#include <ArduinoJson/Numbers/parseNumber.hpp>
7-
#include <ArduinoJson/Variant/VariantImpl.hpp>
5+
#include <ArduinoJson.hpp>
86
#include <catch.hpp>
97

108
using namespace ARDUINOJSON_NAMESPACE;

0 commit comments

Comments
 (0)