Reflection for RapidJSON 0.0.16
Reflection for serializing/deserializing with RapidJSON
Loading...
Searching...
No Matches
jsonreflector.cpp
Go to the documentation of this file.
1#include "../json/reflector.h"
3
4#include <c++utilities/conversion/stringbuilder.h>
5#include <c++utilities/conversion/stringconversion.h>
6#include <c++utilities/io/misc.h>
7#include <c++utilities/tests/testutils.h>
8
9using CppUtilities::operator<<; // must be visible prior to the call site
10#include <cppunit/TestFixture.h>
11#include <cppunit/extensions/HelperMacros.h>
12
13#include <rapidjson/document.h>
14#include <rapidjson/stringbuffer.h>
15#include <rapidjson/writer.h>
16
17#include <iostream>
18#include <map>
19#include <string>
20#include <tuple>
21#include <unordered_map>
22#include <vector>
23
24using namespace std;
25using namespace CPPUNIT_NS;
26using namespace RAPIDJSON_NAMESPACE;
27using namespace CppUtilities;
28using namespace CppUtilities::Literals;
29using namespace ReflectiveRapidJSON;
30
32
33// define some enums and structs for testing serialization
34
35enum SomeEnum {
36 SomeEnumItem1,
37 SomeEnumItem2,
38 SomeEnumItem3,
39};
40
41enum class SomeEnumClass {
42 Item1,
43 Item2,
44 Item3,
45};
46
47struct TestObject : public JsonSerializable<TestObject> {
48 int number;
49 double number2;
50 vector<int> numbers;
51 string text;
52 bool boolean;
53 map<string, int> someMap;
54 unordered_map<string, bool> someHash;
55 multimap<string, int> someMultimap;
56 unordered_multimap<string, int> someMultiHash;
57 set<string> someSet;
58 multiset<string> someMultiset;
59 unordered_set<string> someUnorderedSet;
60 unordered_multiset<string> someUnorderedMultiset;
61 variant<monostate, string, int, float> someVariant;
62 variant<string, int, float> anotherVariant;
63 variant<string, int, float> yetAnotherVariant;
64};
65
66struct NestingObject : public JsonSerializable<NestingObject> {
67 string name;
68 TestObject testObj;
69};
70
71struct NestingArray : public JsonSerializable<NestingArray> {
72 string name;
73 vector<TestObject> testObjects;
74};
75
76// pretend serialization code for structs has been generated
77namespace ReflectiveRapidJSON {
78namespace JsonReflector {
79
80template <> inline void push<TestObject>(const TestObject &reflectable, Value::Object &value, Document::AllocatorType &allocator)
81{
82 push(reflectable.number, "number", value, allocator);
83 push(reflectable.number2, "number2", value, allocator);
84 push(reflectable.numbers, "numbers", value, allocator);
85 push(reflectable.text, "text", value, allocator);
86 push(reflectable.boolean, "boolean", value, allocator);
87 push(reflectable.someMap, "someMap", value, allocator);
88 push(reflectable.someHash, "someHash", value, allocator);
89 push(reflectable.someMultimap, "someMultimap", value, allocator);
90 push(reflectable.someMultiHash, "someMultiHash", value, allocator);
91 push(reflectable.someSet, "someSet", value, allocator);
92 push(reflectable.someMultiset, "someMultiset", value, allocator);
93 push(reflectable.someUnorderedSet, "someUnorderedSet", value, allocator);
94 push(reflectable.someUnorderedMultiset, "someUnorderedMultiset", value, allocator);
95 push(reflectable.someVariant, "someVariant", value, allocator);
96 push(reflectable.anotherVariant, "anotherVariant", value, allocator);
97 push(reflectable.yetAnotherVariant, "yetAnotherVariant", value, allocator);
98}
99
100template <> inline void push<NestingObject>(const NestingObject &reflectable, Value::Object &value, Document::AllocatorType &allocator)
101{
102 push(reflectable.name, "name", value, allocator);
103 push(reflectable.testObj, "testObj", value, allocator);
104}
105
106template <> inline void push<NestingArray>(const NestingArray &reflectable, Value::Object &value, Document::AllocatorType &allocator)
107{
108 push(reflectable.name, "name", value, allocator);
109 push(reflectable.testObjects, "testObjects", value, allocator);
110}
111
112template <>
113inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
114{
115 const char *previousRecord = nullptr;
116 if (errors) {
117 previousRecord = errors->currentRecord;
118 errors->currentRecord = "TestObject";
119 }
120 pull(reflectable.number, "number", value, errors);
121 pull(reflectable.number2, "number2", value, errors);
122 pull(reflectable.numbers, "numbers", value, errors);
123 pull(reflectable.text, "text", value, errors);
124 pull(reflectable.boolean, "boolean", value, errors);
125 pull(reflectable.someMap, "someMap", value, errors);
126 pull(reflectable.someHash, "someHash", value, errors);
127 pull(reflectable.someMultimap, "someMultimap", value, errors);
128 pull(reflectable.someMultiHash, "someMultiHash", value, errors);
129 pull(reflectable.someSet, "someSet", value, errors);
130 pull(reflectable.someMultiset, "someMultiset", value, errors);
131 pull(reflectable.someUnorderedSet, "someUnorderedSet", value, errors);
132 pull(reflectable.someUnorderedMultiset, "someUnorderedMultiset", value, errors);
133 pull(reflectable.someVariant, "someVariant", value, errors);
134 pull(reflectable.anotherVariant, "anotherVariant", value, errors);
135 pull(reflectable.yetAnotherVariant, "yetAnotherVariant", value, errors);
136 if (errors) {
137 errors->currentRecord = previousRecord;
138 }
139}
140
141template <>
142inline void pull<NestingObject>(NestingObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
143{
144 const char *previousRecord = nullptr;
145 if (errors) {
146 previousRecord = errors->currentRecord;
147 errors->currentRecord = "NestingObject";
148 }
149 pull(reflectable.name, "name", value, errors);
150 pull(reflectable.testObj, "testObj", value, errors);
151 if (errors) {
152 errors->currentRecord = previousRecord;
153 }
154}
155
156template <>
157inline void pull<NestingArray>(NestingArray &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
158{
159 const char *previousRecord = nullptr;
160 if (errors) {
161 previousRecord = errors->currentRecord;
162 errors->currentRecord = "NestingArray";
163 }
164 pull(reflectable.name, "name", value, errors);
165 pull(reflectable.testObjects, "testObjects", value, errors);
166 if (errors) {
167 errors->currentRecord = previousRecord;
168 }
169}
170
171} // namespace JsonReflector
172
173// namespace JsonReflector
174} // namespace ReflectiveRapidJSON
175
177
182class JsonReflectorTests : public TestFixture {
183 CPPUNIT_TEST_SUITE(JsonReflectorTests);
184 CPPUNIT_TEST(testSerializePrimitives);
185 CPPUNIT_TEST(testSerializeSimpleObjects);
186 CPPUNIT_TEST(testSerializeNestedObjects);
187 CPPUNIT_TEST(testSerializeUniquePtr);
188 CPPUNIT_TEST(testSerializeSharedPtr);
189 CPPUNIT_TEST(testSerializeOptional);
190 CPPUNIT_TEST(testDeserializePrimitives);
191 CPPUNIT_TEST(testDeserializeSimpleObjects);
192 CPPUNIT_TEST(testDeserializeNestedObjects);
193 CPPUNIT_TEST(testDeserializeUniquePtr);
194 CPPUNIT_TEST(testDeserializeSharedPtr);
195 CPPUNIT_TEST(testDeserializeOptional);
196 CPPUNIT_TEST(testHandlingParseError);
197 CPPUNIT_TEST(testHandlingTypeMismatch);
198 CPPUNIT_TEST_SUITE_END();
199
200public:
201 void setUp() override;
202 void tearDown() override;
203
219
220private:
221};
222
224
228
232
237{
238 Document doc(kArrayType);
239 Document::AllocatorType &alloc = doc.GetAllocator();
240 doc.SetArray();
241 Document::Array array(doc.GetArray());
242
243 // string
244 const string foo("foo"); // mustn't be destroyed until JSON is actually written
245 JsonReflector::push<string>(foo, array, alloc);
246 JsonReflector::push<const char *>("bar", array, alloc);
247 // number
248 JsonReflector::push<int>(25, array, alloc);
249 JsonReflector::push<double>(12.5, array, alloc);
250 // enum
251 JsonReflector::push<SomeEnum>(SomeEnumItem2, array, alloc);
252 JsonReflector::push<SomeEnumClass>(SomeEnumClass::Item2, array, alloc);
253 JsonReflector::push<SomeEnumClass>(SomeEnumClass::Item3, array, alloc);
254 // array
255 JsonReflector::push<vector<const char *>>({ "foo1", "bar1" }, array, alloc);
256 JsonReflector::push<list<const char *>>({ "foo2", "bar2" }, array, alloc);
257 JsonReflector::push<initializer_list<const char *>>({ "foo3", "bar3" }, array, alloc);
258 JsonReflector::push<tuple<int, double>>(make_tuple(2, 413.0), array, alloc);
259 // boolean
260 JsonReflector::push<bool>(true, array, alloc);
261 JsonReflector::push<bool>(false, array, alloc);
262
263 StringBuffer strbuf;
264 Writer<StringBuffer> jsonWriter(strbuf);
265 doc.Accept(jsonWriter);
266 CPPUNIT_ASSERT_EQUAL("[\"foo\",\"bar\",25,12.5,1,1,2,[\"foo1\",\"bar1\"],[\"foo2\",\"bar2\"],[\"foo3\",\"bar3\"],[2,413.0],true,false]"s,
267 string(strbuf.GetString()));
268}
269
274{
275 TestObject testObj;
276 testObj.number = 42;
277 testObj.number2 = 3.141592653589793;
278 testObj.numbers = { 1, 2, 3, 4 };
279 testObj.text = "test";
280 testObj.boolean = false;
281 testObj.someMap = { { "a", 1 }, { "b", 2 } };
282 testObj.someHash = { { "c", true }, { "d", false } };
283 testObj.someMultimap = { { "a", 1 }, { "a", 2 }, { "b", 3 } };
284 testObj.someMultiHash = { { "a", 1 } };
285 testObj.someSet = { "a", "b", "c" };
286 testObj.someMultiset = { "a", "b", "b" };
287 testObj.someUnorderedSet = { "a" };
288 testObj.someUnorderedMultiset = { "b", "b", "b" };
289 testObj.someVariant = std::monostate{};
290 testObj.anotherVariant = "foo";
291 testObj.yetAnotherVariant = 42;
292 CPPUNIT_ASSERT_EQUAL(
293 "{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"d\":false,\"c\":true},\"someMultimap\":{\"a\":[1,2],\"b\":[3]},\"someMultiHash\":{\"a\":[1]},\"someSet\":[\"a\",\"b\",\"c\"],\"someMultiset\":[\"a\",\"b\",\"b\"],\"someUnorderedSet\":[\"a\"],\"someUnorderedMultiset\":[\"b\",\"b\",\"b\"],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"foo\"},\"yetAnotherVariant\":{\"index\":1,\"data\":42}}"s,
294 string(testObj.toJson().GetString()));
295}
296
301{
302 NestingObject nestingObj;
303 nestingObj.name = "nesting";
304 TestObject &testObj = nestingObj.testObj;
305 testObj.number = 42;
306 testObj.number2 = 3.141592653589793;
307 testObj.numbers = { 1, 2, 3, 4 };
308 testObj.text = "test";
309 testObj.boolean = false;
310 CPPUNIT_ASSERT_EQUAL(
311 "{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}}"s,
312 string(nestingObj.toJson().GetString()));
313
314 NestingArray nestingArray;
315 nestingArray.name = "nesting2";
316 nestingArray.testObjects.emplace_back(testObj);
317 nestingArray.testObjects.emplace_back(testObj);
318 nestingArray.testObjects.back().number = 43;
319 CPPUNIT_ASSERT_EQUAL(
320 "{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}},{\"number\":43,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]}"s,
321 string(nestingArray.toJson().GetString()));
322
323 vector<TestObject> nestedInVector;
324 nestedInVector.emplace_back(testObj);
325 CPPUNIT_ASSERT_EQUAL(
326 "[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]"s,
327 string(JsonReflector::toJson(nestedInVector).GetString()));
328}
329
331{
332 Document doc(kArrayType);
333 Document::AllocatorType &alloc = doc.GetAllocator();
334 doc.SetArray();
335 Document::Array array(doc.GetArray());
336
337 const auto str = make_unique<string>("foo");
338 std::unique_ptr<string> nullStr;
339 const auto obj = make_unique<TestObject>();
340 obj->number = 42;
341 obj->number2 = 3.141592653589793;
342 obj->numbers = { 1, 2, 3, 4 };
343 obj->text = "bar";
344 obj->boolean = false;
345
346 JsonReflector::push(str, array, alloc);
347 JsonReflector::push(nullStr, array, alloc);
348 JsonReflector::push(obj, array, alloc);
349
350 StringBuffer strbuf;
351 Writer<StringBuffer> jsonWriter(strbuf);
352 doc.Accept(jsonWriter);
353 CPPUNIT_ASSERT_EQUAL(
354 "[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]"s,
355 string(strbuf.GetString()));
356}
357
359{
360 Document doc(kArrayType);
361 Document::AllocatorType &alloc = doc.GetAllocator();
362 doc.SetArray();
363 Document::Array array(doc.GetArray());
364
365 const auto str = make_shared<string>("foo");
366 std::unique_ptr<string> nullStr;
367 const auto obj = make_shared<TestObject>();
368 obj->number = 42;
369 obj->number2 = 3.141592653589793;
370 obj->numbers = { 1, 2, 3, 4 };
371 obj->text = "bar";
372 obj->boolean = false;
373
374 JsonReflector::push(str, array, alloc);
375 JsonReflector::push(nullStr, array, alloc);
376 JsonReflector::push(obj, array, alloc);
377
378 StringBuffer strbuf;
379 Writer<StringBuffer> jsonWriter(strbuf);
380 doc.Accept(jsonWriter);
381 CPPUNIT_ASSERT_EQUAL(
382 "[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]"s,
383 string(strbuf.GetString()));
384}
385
390{
391 Document doc(kArrayType);
392 Document::AllocatorType &alloc = doc.GetAllocator();
393 doc.SetArray();
394 Document::Array array(doc.GetArray());
395
396 const auto str = make_optional<std::string>("foo");
397 const auto nullStr = std::optional<std::string>();
398
399 JsonReflector::push(str, array, alloc);
400 JsonReflector::push(nullStr, array, alloc);
401
402 StringBuffer strbuf;
403 Writer<StringBuffer> jsonWriter(strbuf);
404 doc.Accept(jsonWriter);
405 CPPUNIT_ASSERT_EQUAL("[\"foo\",null]"s, std::string(strbuf.GetString()));
406}
407
412{
413 Document doc(kArrayType);
414
415 doc.Parse("[\"a\", 5, 5.0, 5e6, 4, \"test\", true, 4.125, false]");
416 auto array = doc.GetArray().begin();
417
418 string str1, str2;
419 int int1 = 0, int2 = 0;
420 bool bool1 = false, bool2 = true;
421 float float1 = 0.0f, float2 = 0.0f;
422 double double1 = 0.0;
424 JsonReflector::pull(str1, array, &errors);
425 JsonReflector::pull(int1, array, &errors);
426 JsonReflector::pull(int2, array, &errors);
427 JsonReflector::pull(float1, array, &errors);
428 JsonReflector::pull(float2, array, &errors);
429 JsonReflector::pull(str2, array, &errors);
430 JsonReflector::pull(bool1, array, &errors);
431 JsonReflector::pull(double1, array, &errors);
432 JsonReflector::pull(bool2, array, &errors);
433
434 CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
435 CPPUNIT_ASSERT_EQUAL("a"s, str1);
436 CPPUNIT_ASSERT_EQUAL(5, int1);
437 CPPUNIT_ASSERT_EQUAL(5, int2);
438 CPPUNIT_ASSERT_EQUAL(5e6f, float1);
439 CPPUNIT_ASSERT_EQUAL(4.f, float2);
440 CPPUNIT_ASSERT_EQUAL("test"s, str2);
441 CPPUNIT_ASSERT_EQUAL(true, bool1);
442 CPPUNIT_ASSERT_EQUAL(4.125, double1);
443 CPPUNIT_ASSERT_EQUAL(false, bool2);
444
445 // deserialize primitives as tuple
446 tuple<string, int, int, float, float, string, bool, double, bool> arrayAsTuple;
447 JsonReflector::pull(arrayAsTuple, doc, &errors);
448 CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
449 CPPUNIT_ASSERT_EQUAL("a"s, get<0>(arrayAsTuple));
450 CPPUNIT_ASSERT_EQUAL(5, get<1>(arrayAsTuple));
451 CPPUNIT_ASSERT_EQUAL(5, get<2>(arrayAsTuple));
452 CPPUNIT_ASSERT_EQUAL(5e6f, get<3>(arrayAsTuple));
453 CPPUNIT_ASSERT_EQUAL(4.f, get<4>(arrayAsTuple));
454 CPPUNIT_ASSERT_EQUAL("test"s, get<5>(arrayAsTuple));
455 CPPUNIT_ASSERT_EQUAL(true, get<6>(arrayAsTuple));
456 CPPUNIT_ASSERT_EQUAL(4.125, get<7>(arrayAsTuple));
457 CPPUNIT_ASSERT_EQUAL(false, get<8>(arrayAsTuple));
458 tuple<string, int> anotherTuple;
459 JsonReflector::pull(anotherTuple, doc, &errors);
460 CPPUNIT_ASSERT_EQUAL(1_st, errors.size());
461 CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::ArraySizeMismatch, errors.front().kind);
462}
463
468{
469 const auto testObj
470 = TestObject::fromJson("{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":"
471 "false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"c\":true,\"d\":false},\"someMultimap\":{\"a\":[1,2],\"b\":[3]},"
472 "\"someMultiHash\":{\"a\":[4,5],\"b\":[6]},"
473 "\"someSet\":[\"a\",\"b\"],\"someMultiset\":["
474 "\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"b\"],\"someUnorderedMultiset\":[\"a\",\"a\"],\"someVariant\":{\"index\":0,"
475 "\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"foo\"},\"yetAnotherVariant\":{\"index\":1,\"data\":42}}");
476
477 CPPUNIT_ASSERT_EQUAL(42, testObj.number);
478 CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
479 CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
480 CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
481 CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
482 const map<string, int> expectedMap{ { "a", 1 }, { "b", 2 } };
483 CPPUNIT_ASSERT_EQUAL(expectedMap, testObj.someMap);
484 const unordered_map<string, bool> expectedHash{ { "c", true }, { "d", false } };
485 CPPUNIT_ASSERT_EQUAL(expectedHash, testObj.someHash);
486 const multimap<string, int> expectedMultiMap{ { "a", 1 }, { "a", 2 }, { "b", 3 } };
487 CPPUNIT_ASSERT_EQUAL(expectedMultiMap, testObj.someMultimap);
488 const unordered_multimap<string, int> expectedUnorderedMultiMap{ { "a", 4 }, { "a", 5 }, { "b", 6 } };
489 CPPUNIT_ASSERT_EQUAL(expectedUnorderedMultiMap, testObj.someMultiHash);
490 CPPUNIT_ASSERT_EQUAL(set<string>({ "a", "b" }), testObj.someSet);
491 CPPUNIT_ASSERT_EQUAL(multiset<string>({ "a", "a" }), testObj.someMultiset);
492 CPPUNIT_ASSERT_EQUAL(unordered_set<string>({ "a", "b" }), testObj.someUnorderedSet);
493 CPPUNIT_ASSERT_EQUAL(unordered_multiset<string>({ "a", "a" }), testObj.someUnorderedMultiset);
494 CPPUNIT_ASSERT_EQUAL(0_st, testObj.someVariant.index());
495 CPPUNIT_ASSERT_EQUAL(0_st, testObj.anotherVariant.index());
496 CPPUNIT_ASSERT_EQUAL("foo"s, std::get<0>(testObj.anotherVariant));
497 CPPUNIT_ASSERT_EQUAL(1_st, testObj.yetAnotherVariant.index());
498 CPPUNIT_ASSERT_EQUAL(42, std::get<1>(testObj.yetAnotherVariant));
499}
500
505{
507 const NestingObject nestingObj(NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,"
508 "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}}",
509 &errors));
510 const TestObject &testObj = nestingObj.testObj;
511 CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
512 CPPUNIT_ASSERT_EQUAL("nesting"s, nestingObj.name);
513 CPPUNIT_ASSERT_EQUAL(42, testObj.number);
514 CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
515 CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
516 CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
517 CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
518
519 const NestingArray nestingArray(NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,"
520 "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},{\"number\":43,\"number2\":3."
521 "141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}]}",
522 &errors));
523 const vector<TestObject> &testObjects = nestingArray.testObjects;
524 CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
525 CPPUNIT_ASSERT_EQUAL("nesting2"s, nestingArray.name);
526 CPPUNIT_ASSERT_EQUAL(2_st, testObjects.size());
527 CPPUNIT_ASSERT_EQUAL(42, testObjects[0].number);
528 CPPUNIT_ASSERT_EQUAL(43, testObjects[1].number);
529 for (const TestObject &nestedTestObj : testObjects) {
530 CPPUNIT_ASSERT_EQUAL(3.141592653589793, nestedTestObj.number2);
531 CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), nestedTestObj.numbers);
532 CPPUNIT_ASSERT_EQUAL("test"s, nestedTestObj.text);
533 CPPUNIT_ASSERT_EQUAL(false, nestedTestObj.boolean);
534 }
535
536 const auto nestedInVector(JsonReflector::fromJson<vector<TestObject>>(
537 "[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]",
538 &errors));
539 CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
540 CPPUNIT_ASSERT_EQUAL(1_st, nestedInVector.size());
541 CPPUNIT_ASSERT_EQUAL(42, nestedInVector[0].number);
542 CPPUNIT_ASSERT_EQUAL(4_st, nestedInVector[0].numbers.size());
543 CPPUNIT_ASSERT_EQUAL("test"s, nestedInVector[0].text);
544}
545
550{
551 Document doc(kArrayType);
552 doc.Parse("[\"foo\",null,{\"text\":\"bar\"}]");
553 auto array = doc.GetArray().begin();
554
555 unique_ptr<string> str;
556 unique_ptr<string> nullStr;
557 unique_ptr<TestObject> obj;
559 JsonReflector::pull(str, array, &errors);
560 JsonReflector::pull(nullStr, array, &errors);
561 JsonReflector::pull(obj, array, &errors);
562
563 CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
564 CPPUNIT_ASSERT(str);
565 CPPUNIT_ASSERT_EQUAL("foo"s, *str);
566 CPPUNIT_ASSERT(!nullStr);
567 CPPUNIT_ASSERT(obj);
568 CPPUNIT_ASSERT_EQUAL("bar"s, obj->text);
569}
570
572{
573 Document doc(kArrayType);
574 doc.Parse("[\"foo\",null,{\"text\":\"bar\"}]");
575 auto array = doc.GetArray().begin();
576
577 shared_ptr<string> str;
578 shared_ptr<string> nullStr;
579 shared_ptr<TestObject> obj;
581 JsonReflector::pull(str, array, &errors);
582 JsonReflector::pull(nullStr, array, &errors);
583 JsonReflector::pull(obj, array, &errors);
584
585 CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
586 CPPUNIT_ASSERT(str);
587 CPPUNIT_ASSERT_EQUAL("foo"s, *str);
588 CPPUNIT_ASSERT(!nullStr);
589 CPPUNIT_ASSERT(obj);
590 CPPUNIT_ASSERT_EQUAL("bar"s, obj->text);
591}
592
594{
595 Document doc(kArrayType);
596 doc.Parse("[\"foo\",null]");
597 auto array = doc.GetArray().begin();
598
599 optional<string> str = "foo"s;
600 optional<string> nullStr;
602 JsonReflector::pull(str, array, &errors);
603 CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
604 CPPUNIT_ASSERT(str.has_value());
605 CPPUNIT_ASSERT_EQUAL("foo"s, *str);
606 CPPUNIT_ASSERT(!nullStr.has_value());
607}
608
613{
614 try {
615 NestingObject::fromJson("{\"name\":nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
616 "\"test\",\"boolean\":false}}");
617 CPPUNIT_FAIL("expected ParseResult thrown");
618 } catch (const RAPIDJSON_NAMESPACE::ParseResult &res) {
619 CPPUNIT_ASSERT_EQUAL(RAPIDJSON_NAMESPACE::kParseErrorValueInvalid, res.Code());
620 CPPUNIT_ASSERT_EQUAL(9_st, res.Offset());
621 }
622}
623
628{
630 NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,"
631 "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},{\"number\":43,\"number2\":3."
632 "141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}]}",
633 &errors);
634 CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
635
636 NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":\"42\",\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
637 "\"test\",\"boolean\":false,\"someSet\":[\"a\",\"a\"],\"someMultiset\":[\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"a\"],"
638 "\"someUnorderedMultiset\":[\"a\",\"a\"]}}",
639 &errors);
640 CPPUNIT_ASSERT_EQUAL(3_st, errors.size());
641 CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
642 CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors.front().expectedType);
643 CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.front().actualType);
644 CPPUNIT_ASSERT_EQUAL("number"s, string(errors.front().member));
645 CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors.front().record));
646 CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::UnexpectedDuplicate, errors[1].kind);
647 CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[1].expectedType);
648 CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[1].actualType);
649 CPPUNIT_ASSERT_EQUAL("someSet"s, string(errors[1].member));
650 CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[1].record));
651 CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::UnexpectedDuplicate, errors[2].kind);
652 CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[2].expectedType);
653 CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[2].actualType);
654 CPPUNIT_ASSERT_EQUAL("someUnorderedSet"s, string(errors[2].member));
655 CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[2].record));
656 errors.clear();
657
658 NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":1,\"text\":"
659 "\"test\",\"boolean\":false}}",
660 &errors);
661 CPPUNIT_ASSERT_EQUAL(1_st, errors.size());
662 CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
663 CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors.front().expectedType);
664 CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors.front().actualType);
665 CPPUNIT_ASSERT_EQUAL("numbers"s, string(errors.front().member));
666 CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors.front().record));
667 errors.clear();
668
669 NestingObject::fromJson("{\"name\":[],\"testObj\":\"this is not an object\"}", &errors);
670 CPPUNIT_ASSERT_EQUAL(2_st, errors.size());
671 CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
672 CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.front().expectedType);
673 CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors.front().actualType);
674 CPPUNIT_ASSERT_EQUAL("name"s, string(errors.front().member));
675 CPPUNIT_ASSERT_EQUAL("NestingObject"s, string(errors.front().record));
676 CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.back().kind);
677 CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors.back().expectedType);
678 CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.back().actualType);
679 CPPUNIT_ASSERT_EQUAL("testObj"s, string(errors.back().member));
680 CPPUNIT_ASSERT_EQUAL("NestingObject"s, string(errors.back().record));
681 errors.clear();
682
683 const NestingArray nestingArray(
684 NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[25,{\"number\":42,\"number2\":3.141592653589793,"
685 "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},\"foo\",{\"number\":43,\"number2\":3."
686 "141592653589793,\"numbers\":[1,2,3,4,\"bar\"],\"text\":\"test\",\"boolean\":false}]}",
687 &errors));
688 CPPUNIT_ASSERT_EQUAL(3_st, errors.size());
689 CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[0].kind);
690 CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors[0].expectedType);
691 CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors[0].actualType);
692 CPPUNIT_ASSERT_EQUAL("testObjects"s, string(errors[0].member));
693 CPPUNIT_ASSERT_EQUAL("NestingArray"s, string(errors[0].record));
694 CPPUNIT_ASSERT_EQUAL(0_st, errors[0].index);
695 CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[1].kind);
696 CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors[1].expectedType);
697 CPPUNIT_ASSERT_EQUAL(JsonType::String, errors[1].actualType);
698 CPPUNIT_ASSERT_EQUAL(2_st, errors[1].index);
699 CPPUNIT_ASSERT_EQUAL("testObjects"s, string(errors[1].member));
700 CPPUNIT_ASSERT_EQUAL("NestingArray"s, string(errors[1].record));
701 CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[2].kind);
702 CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors[2].expectedType);
703 CPPUNIT_ASSERT_EQUAL(JsonType::String, errors[2].actualType);
704 CPPUNIT_ASSERT_EQUAL("numbers"s, string(errors[2].member));
705 CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[2].record));
706 CPPUNIT_ASSERT_EQUAL(4_st, errors[2].index);
707 errors.clear();
708
709 errors.throwOn = JsonDeserializationErrors::ThrowOn::TypeMismatch;
710 CPPUNIT_ASSERT_THROW(NestingObject::fromJson("{\"name\":[],\"testObj\":\"this is not an object\"}", &errors), JsonDeserializationError);
711}
The JsonReflectorTests class tests RapidJSON wrapper which is used to ease code generation.
void tearDown() override
void testDeserializeNestedObjects()
Tests deserializing nested objects and arrays.
void testSerializeSimpleObjects()
Tests serializing objects.
void testSerializePrimitives()
Tests serializing strings, numbers, arrays and boolean.
void testSerializeNestedObjects()
Tests serializing nested object and arrays.
void testDeserializePrimitives()
Tests deserializing strings, numbers (int, float, double) and boolean.
void setUp() override
void testHandlingParseError()
Tests whether RAPIDJSON_NAMESPACE::ParseResult is thrown correctly when passing invalid JSON to fromJ...
void testDeserializeUniquePtr()
Tests deserializing std::optional.
void testHandlingTypeMismatch()
Tests whether errors are added on type mismatch and in other cases.
void testSerializeOptional()
Tests serializing std::optional.
void testDeserializeSimpleObjects()
Tests deserializing simple objects.
Contains functions to (de)serialize basic types such as int, double, bool, std::string,...
Contains only the definition of the JsonSerializable template class which makes the reflection access...
CPPUNIT_TEST_SUITE_REGISTRATION(JsonReflectorTests)
RAPIDJSON_NAMESPACE::StringBuffer toJson(const Type &reflectable)
Serializes the specified reflectable.
Definition reflector.h:1086
void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue< RAPIDJSON_NAMESPACE::UTF8< char > >::ConstObject &value, JsonDeserializationErrors *errors)
Pulls the reflectable which has a custom type from the specified object.
Type fromJson(const char *json, std::size_t jsonSize, JsonDeserializationErrors *errors=nullptr)
Deserializes the specified JSON to.
Definition reflector.h:1098
void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
Pushes the specified reflectable to the specified value.
Definition reflector.h:140
The JsonDeserializationError struct describes any errors of fromJson() except such caused by invalid ...
The JsonDeserializationErrors struct can be passed to fromJson() for error handling.
const char * currentRecord
The name of the class or struct which is currently being processed.
enum ReflectiveRapidJSON::JsonDeserializationErrors::ThrowOn throwOn
The JsonSerializable class provides the CRTP-base for (de)serializable objects.