Reflection for RapidJSON  0.0.8
Reflection for serializing/deserializing with RapidJSON
jsonreflector.cpp
Go to the documentation of this file.
1 #include "../json/reflector.h"
2 #include "../json/serializable.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 
9 using TestUtilities::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 
24 using namespace std;
25 using namespace CPPUNIT_NS;
26 using namespace RAPIDJSON_NAMESPACE;
27 using namespace IoUtilities;
28 using namespace ConversionUtilities;
29 using namespace TestUtilities;
30 using namespace TestUtilities::Literals;
31 using namespace ReflectiveRapidJSON;
32 
34 
35 // define some enums and structs for testing serialization
36 
37 enum SomeEnum {
38  SomeEnumItem1,
39  SomeEnumItem2,
40  SomeEnumItem3,
41 };
42 
43 enum class SomeEnumClass {
44  Item1,
45  Item2,
46  Item3,
47 };
48 
49 struct TestObject : public JsonSerializable<TestObject> {
50  int number;
51  double number2;
52  vector<int> numbers;
53  string text;
54  bool boolean;
55  map<string, int> someMap;
56  unordered_map<string, bool> someHash;
57  set<string> someSet;
58  multiset<string> someMultiset;
59  unordered_set<string> someUnorderedSet;
60  unordered_multiset<string> someUnorderedMultiset;
61 };
62 
63 struct NestingObject : public JsonSerializable<NestingObject> {
64  string name;
65  TestObject testObj;
66 };
67 
68 struct NestingArray : public JsonSerializable<NestingArray> {
69  string name;
70  vector<TestObject> testObjects;
71 };
72 
73 // pretend serialization code for structs has been generated
74 namespace ReflectiveRapidJSON {
75 namespace JsonReflector {
76 
77 template <> inline void push<TestObject>(const TestObject &reflectable, Value::Object &value, Document::AllocatorType &allocator)
78 {
79  push(reflectable.number, "number", value, allocator);
80  push(reflectable.number2, "number2", value, allocator);
81  push(reflectable.numbers, "numbers", value, allocator);
82  push(reflectable.text, "text", value, allocator);
83  push(reflectable.boolean, "boolean", value, allocator);
84  push(reflectable.someMap, "someMap", value, allocator);
85  push(reflectable.someHash, "someHash", value, allocator);
86  push(reflectable.someSet, "someSet", value, allocator);
87  push(reflectable.someMultiset, "someMultiset", value, allocator);
88  push(reflectable.someUnorderedSet, "someUnorderedSet", value, allocator);
89  push(reflectable.someUnorderedMultiset, "someUnorderedMultiset", value, allocator);
90 }
91 
92 template <> inline void push<NestingObject>(const NestingObject &reflectable, Value::Object &value, Document::AllocatorType &allocator)
93 {
94  push(reflectable.name, "name", value, allocator);
95  push(reflectable.testObj, "testObj", value, allocator);
96 }
97 
98 template <> inline void push<NestingArray>(const NestingArray &reflectable, Value::Object &value, Document::AllocatorType &allocator)
99 {
100  push(reflectable.name, "name", value, allocator);
101  push(reflectable.testObjects, "testObjects", value, allocator);
102 }
103 
104 template <>
105 inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
106 {
107  const char *previousRecord;
108  if (errors) {
109  previousRecord = errors->currentRecord;
110  errors->currentRecord = "TestObject";
111  }
112  pull(reflectable.number, "number", value, errors);
113  pull(reflectable.number2, "number2", value, errors);
114  pull(reflectable.numbers, "numbers", value, errors);
115  pull(reflectable.text, "text", value, errors);
116  pull(reflectable.boolean, "boolean", value, errors);
117  pull(reflectable.someMap, "someMap", value, errors);
118  pull(reflectable.someHash, "someHash", value, errors);
119  pull(reflectable.someSet, "someSet", value, errors);
120  pull(reflectable.someMultiset, "someMultiset", value, errors);
121  pull(reflectable.someUnorderedSet, "someUnorderedSet", value, errors);
122  pull(reflectable.someUnorderedMultiset, "someUnorderedMultiset", value, errors);
123  if (errors) {
124  errors->currentRecord = previousRecord;
125  }
126 }
127 
128 template <>
129 inline void pull<NestingObject>(NestingObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
130 {
131  const char *previousRecord;
132  if (errors) {
133  previousRecord = errors->currentRecord;
134  errors->currentRecord = "NestingObject";
135  }
136  pull(reflectable.name, "name", value, errors);
137  pull(reflectable.testObj, "testObj", value, errors);
138  if (errors) {
139  errors->currentRecord = previousRecord;
140  }
141 }
142 
143 template <>
144 inline void pull<NestingArray>(NestingArray &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
145 {
146  const char *previousRecord;
147  if (errors) {
148  previousRecord = errors->currentRecord;
149  errors->currentRecord = "NestingArray";
150  }
151  pull(reflectable.name, "name", value, errors);
152  pull(reflectable.testObjects, "testObjects", value, errors);
153  if (errors) {
154  errors->currentRecord = previousRecord;
155  }
156 }
157 
158 } // namespace JsonReflector
159 
160 // namespace JsonReflector
161 } // namespace ReflectiveRapidJSON
162 
164 
169 class JsonReflectorTests : public TestFixture {
170  CPPUNIT_TEST_SUITE(JsonReflectorTests);
171  CPPUNIT_TEST(testSerializePrimitives);
172  CPPUNIT_TEST(testSerializeSimpleObjects);
173  CPPUNIT_TEST(testSerializeNestedObjects);
174  CPPUNIT_TEST(testSerializeUniquePtr);
175  CPPUNIT_TEST(testSerializeSharedPtr);
176  CPPUNIT_TEST(testDeserializePrimitives);
177  CPPUNIT_TEST(testDeserializeSimpleObjects);
178  CPPUNIT_TEST(testDeserializeNestedObjects);
179  CPPUNIT_TEST(testDeserializeUniquePtr);
180  CPPUNIT_TEST(testDeserializeSharedPtr);
181  CPPUNIT_TEST(testHandlingParseError);
182  CPPUNIT_TEST(testHandlingTypeMismatch);
183  CPPUNIT_TEST_SUITE_END();
184 
185 public:
186  void setUp();
187  void tearDown();
188 
189  void experiment();
190  void testSerializePrimitives();
191  void testSerializeSimpleObjects();
192  void testSerializeNestedObjects();
193  void testSerializeUniquePtr();
194  void testSerializeSharedPtr();
195  void testDeserializePrimitives();
196  void testDeserializeSimpleObjects();
197  void testDeserializeNestedObjects();
198  void testDeserializeUniquePtr();
199  void testDeserializeSharedPtr();
200  void testHandlingParseError();
201  void testHandlingTypeMismatch();
202 
203 private:
204 };
205 
207 
209 {
210 }
211 
213 {
214 }
215 
220 {
221  Document doc(kArrayType);
222  Document::AllocatorType &alloc = doc.GetAllocator();
223  doc.SetArray();
224  Document::Array array(doc.GetArray());
225 
226  // string
227  const string foo("foo"); // musn't be destroyed until JSON is actually written
228  JsonReflector::push<string>(foo, array, alloc);
229  JsonReflector::push<const char *>("bar", array, alloc);
230  // number
231  JsonReflector::push<int>(25, array, alloc);
232  JsonReflector::push<double>(12.5, array, alloc);
233  // enum
234  JsonReflector::push<SomeEnum>(SomeEnumItem2, array, alloc);
235  JsonReflector::push<SomeEnumClass>(SomeEnumClass::Item2, array, alloc);
236  JsonReflector::push<SomeEnumClass>(SomeEnumClass::Item3, array, alloc);
237  // array
238  JsonReflector::push<vector<const char *>>({ "foo1", "bar1" }, array, alloc);
239  JsonReflector::push<list<const char *>>({ "foo2", "bar2" }, array, alloc);
240  JsonReflector::push<initializer_list<const char *>>({ "foo3", "bar3" }, array, alloc);
241  JsonReflector::push<tuple<int, double>>(make_tuple(2, 413.0), array, alloc);
242  // boolean
243  JsonReflector::push<bool>(true, array, alloc);
244  JsonReflector::push<bool>(false, array, alloc);
245 
246  StringBuffer strbuf;
247  Writer<StringBuffer> jsonWriter(strbuf);
248  doc.Accept(jsonWriter);
249  CPPUNIT_ASSERT_EQUAL("[\"foo\",\"bar\",25,12.5,1,1,2,[\"foo1\",\"bar1\"],[\"foo2\",\"bar2\"],[\"foo3\",\"bar3\"],[2,413.0],true,false]"s,
250  string(strbuf.GetString()));
251 }
252 
257 {
258  TestObject testObj;
259  testObj.number = 42;
260  testObj.number2 = 3.141592653589793;
261  testObj.numbers = { 1, 2, 3, 4 };
262  testObj.text = "test";
263  testObj.boolean = false;
264  testObj.someMap = { { "a", 1 }, { "b", 2 } };
265  testObj.someHash = { { "c", true }, { "d", false } };
266  testObj.someSet = { "a", "b", "c" };
267  testObj.someMultiset = { "a", "b", "b" };
268  testObj.someUnorderedSet = { "a" };
269  testObj.someUnorderedMultiset = { "b", "b", "b" };
270  CPPUNIT_ASSERT_EQUAL(
271  "{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"d\":false,\"c\":true},\"someSet\":[\"a\",\"b\",\"c\"],\"someMultiset\":[\"a\",\"b\",\"b\"],\"someUnorderedSet\":[\"a\"],\"someUnorderedMultiset\":[\"b\",\"b\",\"b\"]}"s,
272  string(testObj.toJson().GetString()));
273 }
274 
279 {
280  NestingObject nestingObj;
281  nestingObj.name = "nesting";
282  TestObject &testObj = nestingObj.testObj;
283  testObj.number = 42;
284  testObj.number2 = 3.141592653589793;
285  testObj.numbers = { 1, 2, 3, 4 };
286  testObj.text = "test";
287  testObj.boolean = false;
288  CPPUNIT_ASSERT_EQUAL(
289  "{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}}"s,
290  string(nestingObj.toJson().GetString()));
291 
292  NestingArray nestingArray;
293  nestingArray.name = "nesting2";
294  nestingArray.testObjects.emplace_back(testObj);
295  nestingArray.testObjects.emplace_back(testObj);
296  nestingArray.testObjects.back().number = 43;
297  CPPUNIT_ASSERT_EQUAL(
298  "{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]},{\"number\":43,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}]}"s,
299  string(nestingArray.toJson().GetString()));
300 
301  vector<TestObject> nestedInVector;
302  nestedInVector.emplace_back(testObj);
303  CPPUNIT_ASSERT_EQUAL(
304  "[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}]"s,
305  string(JsonReflector::toJson(nestedInVector).GetString()));
306 }
307 
309 {
310  Document doc(kArrayType);
311  Document::AllocatorType &alloc = doc.GetAllocator();
312  doc.SetArray();
313  Document::Array array(doc.GetArray());
314 
315  const auto str = make_unique<string>("foo");
316  std::unique_ptr<string> nullStr;
317  const auto obj = make_unique<TestObject>();
318  obj->number = 42;
319  obj->number2 = 3.141592653589793;
320  obj->numbers = { 1, 2, 3, 4 };
321  obj->text = "bar";
322  obj->boolean = false;
323 
324  JsonReflector::push(str, array, alloc);
325  JsonReflector::push(nullStr, array, alloc);
326  JsonReflector::push(obj, array, alloc);
327 
328  StringBuffer strbuf;
329  Writer<StringBuffer> jsonWriter(strbuf);
330  doc.Accept(jsonWriter);
331  CPPUNIT_ASSERT_EQUAL(
332  "[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}]"s,
333  string(strbuf.GetString()));
334 }
335 
337 {
338  Document doc(kArrayType);
339  Document::AllocatorType &alloc = doc.GetAllocator();
340  doc.SetArray();
341  Document::Array array(doc.GetArray());
342 
343  const auto str = make_shared<string>("foo");
344  std::unique_ptr<string> nullStr;
345  const auto obj = make_shared<TestObject>();
346  obj->number = 42;
347  obj->number2 = 3.141592653589793;
348  obj->numbers = { 1, 2, 3, 4 };
349  obj->text = "bar";
350  obj->boolean = false;
351 
352  JsonReflector::push(str, array, alloc);
353  JsonReflector::push(nullStr, array, alloc);
354  JsonReflector::push(obj, array, alloc);
355 
356  StringBuffer strbuf;
357  Writer<StringBuffer> jsonWriter(strbuf);
358  doc.Accept(jsonWriter);
359  CPPUNIT_ASSERT_EQUAL(
360  "[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}]"s,
361  string(strbuf.GetString()));
362 }
363 
368 {
369  Document doc(kArrayType);
370 
371  doc.Parse("[\"a\", 5, 5.0, 5e6, 4, \"test\", true, 4.125, false]");
372  auto array = doc.GetArray().begin();
373 
374  string str1, str2;
375  int int1 = 0, int2 = 0;
376  bool bool1 = false, bool2 = true;
377  float float1 = 0.0f, float2 = 0.0f;
378  double double1 = 0.0;
380  JsonReflector::pull(str1, array, &errors);
381  JsonReflector::pull(int1, array, &errors);
382  JsonReflector::pull(int2, array, &errors);
383  JsonReflector::pull(float1, array, &errors);
384  JsonReflector::pull(float2, array, &errors);
385  JsonReflector::pull(str2, array, &errors);
386  JsonReflector::pull(bool1, array, &errors);
387  JsonReflector::pull(double1, array, &errors);
388  JsonReflector::pull(bool2, array, &errors);
389 
390  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
391  CPPUNIT_ASSERT_EQUAL("a"s, str1);
392  CPPUNIT_ASSERT_EQUAL(5, int1);
393  CPPUNIT_ASSERT_EQUAL(5, int2);
394  CPPUNIT_ASSERT_EQUAL(5e6f, float1);
395  CPPUNIT_ASSERT_EQUAL(4.f, float2);
396  CPPUNIT_ASSERT_EQUAL("test"s, str2);
397  CPPUNIT_ASSERT_EQUAL(true, bool1);
398  CPPUNIT_ASSERT_EQUAL(4.125, double1);
399  CPPUNIT_ASSERT_EQUAL(false, bool2);
400 
401  // deserialize primitives as tuple
402  tuple<string, int, int, float, float, string, bool, double, bool> arrayAsTuple;
403  JsonReflector::pull(arrayAsTuple, doc, &errors);
404  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
405  CPPUNIT_ASSERT_EQUAL("a"s, get<0>(arrayAsTuple));
406  CPPUNIT_ASSERT_EQUAL(5, get<1>(arrayAsTuple));
407  CPPUNIT_ASSERT_EQUAL(5, get<2>(arrayAsTuple));
408  CPPUNIT_ASSERT_EQUAL(5e6f, get<3>(arrayAsTuple));
409  CPPUNIT_ASSERT_EQUAL(4.f, get<4>(arrayAsTuple));
410  CPPUNIT_ASSERT_EQUAL("test"s, get<5>(arrayAsTuple));
411  CPPUNIT_ASSERT_EQUAL(true, get<6>(arrayAsTuple));
412  CPPUNIT_ASSERT_EQUAL(4.125, get<7>(arrayAsTuple));
413  CPPUNIT_ASSERT_EQUAL(false, get<8>(arrayAsTuple));
414  tuple<string, int> anotherTuple;
415  JsonReflector::pull(anotherTuple, doc, &errors);
416  CPPUNIT_ASSERT_EQUAL(1_st, errors.size());
417  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::ArraySizeMismatch, errors.front().kind);
418 }
419 
424 {
425  const TestObject testObj(
426  TestObject::fromJson("{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":"
427  "false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"c\":true,\"d\":false},\"someSet\":[\"a\",\"b\"],\"someMultiset\":["
428  "\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"b\"],\"someUnorderedMultiset\":[\"a\",\"a\"]}"));
429 
430  CPPUNIT_ASSERT_EQUAL(42, testObj.number);
431  CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
432  CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
433  CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
434  CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
435  const map<string, int> expectedMap{ { "a", 1 }, { "b", 2 } };
436  CPPUNIT_ASSERT_EQUAL(expectedMap, testObj.someMap);
437  const unordered_map<string, bool> expectedHash{ { "c", true }, { "d", false } };
438  CPPUNIT_ASSERT_EQUAL(expectedHash, testObj.someHash);
439  CPPUNIT_ASSERT_EQUAL(set<string>({ "a", "b" }), testObj.someSet);
440  CPPUNIT_ASSERT_EQUAL(multiset<string>({ "a", "a" }), testObj.someMultiset);
441  CPPUNIT_ASSERT_EQUAL(unordered_set<string>({ "a", "b" }), testObj.someUnorderedSet);
442  CPPUNIT_ASSERT_EQUAL(unordered_multiset<string>({ "a", "a" }), testObj.someUnorderedMultiset);
443 }
444 
449 {
451  const NestingObject nestingObj(NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,"
452  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}}",
453  &errors));
454  const TestObject &testObj = nestingObj.testObj;
455  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
456  CPPUNIT_ASSERT_EQUAL("nesting"s, nestingObj.name);
457  CPPUNIT_ASSERT_EQUAL(42, testObj.number);
458  CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
459  CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
460  CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
461  CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
462 
463  const NestingArray nestingArray(NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,"
464  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},{\"number\":43,\"number2\":3."
465  "141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}]}",
466  &errors));
467  const vector<TestObject> &testObjects = nestingArray.testObjects;
468  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
469  CPPUNIT_ASSERT_EQUAL("nesting2"s, nestingArray.name);
470  CPPUNIT_ASSERT_EQUAL(2_st, testObjects.size());
471  CPPUNIT_ASSERT_EQUAL(42, testObjects[0].number);
472  CPPUNIT_ASSERT_EQUAL(43, testObjects[1].number);
473  for (const TestObject &testObj : testObjects) {
474  CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
475  CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
476  CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
477  CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
478  }
479 
480  const auto nestedInVector(JsonReflector::fromJson<vector<TestObject>>(
481  "[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]",
482  &errors));
483  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
484  CPPUNIT_ASSERT_EQUAL(1_st, nestedInVector.size());
485  CPPUNIT_ASSERT_EQUAL(42, nestedInVector[0].number);
486  CPPUNIT_ASSERT_EQUAL(4_st, nestedInVector[0].numbers.size());
487  CPPUNIT_ASSERT_EQUAL("test"s, nestedInVector[0].text);
488 }
489 
491 {
492  Document doc(kArrayType);
493  doc.Parse("[\"foo\",null,{\"text\":\"bar\"}]");
494  auto array = doc.GetArray().begin();
495 
496  unique_ptr<string> str;
497  unique_ptr<string> nullStr;
498  unique_ptr<TestObject> obj;
500  JsonReflector::pull(str, array, &errors);
501  JsonReflector::pull(nullStr, array, &errors);
502  JsonReflector::pull(obj, array, &errors);
503 
504  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
505  CPPUNIT_ASSERT(str);
506  CPPUNIT_ASSERT_EQUAL("foo"s, *str);
507  CPPUNIT_ASSERT(!nullStr);
508  CPPUNIT_ASSERT(obj);
509  CPPUNIT_ASSERT_EQUAL("bar"s, obj->text);
510 }
511 
513 {
514  Document doc(kArrayType);
515  doc.Parse("[\"foo\",null,{\"text\":\"bar\"}]");
516  auto array = doc.GetArray().begin();
517 
518  shared_ptr<string> str;
519  shared_ptr<string> nullStr;
520  shared_ptr<TestObject> obj;
522  JsonReflector::pull(str, array, &errors);
523  JsonReflector::pull(nullStr, array, &errors);
524  JsonReflector::pull(obj, array, &errors);
525 
526  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
527  CPPUNIT_ASSERT(str);
528  CPPUNIT_ASSERT_EQUAL("foo"s, *str);
529  CPPUNIT_ASSERT(!nullStr);
530  CPPUNIT_ASSERT(obj);
531  CPPUNIT_ASSERT_EQUAL("bar"s, obj->text);
532 }
533 
538 {
539  try {
540  NestingObject::fromJson("{\"name\":nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
541  "\"test\",\"boolean\":false}}");
542  CPPUNIT_FAIL("expected ParseResult thrown");
543  } catch (const RAPIDJSON_NAMESPACE::ParseResult &res) {
544  CPPUNIT_ASSERT_EQUAL(RAPIDJSON_NAMESPACE::kParseErrorValueInvalid, res.Code());
545  CPPUNIT_ASSERT_EQUAL(9_st, res.Offset());
546  }
547 }
548 
553 {
555  NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,"
556  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},{\"number\":43,\"number2\":3."
557  "141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}]}",
558  &errors);
559  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
560 
561  NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":\"42\",\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
562  "\"test\",\"boolean\":false,\"someSet\":[\"a\",\"a\"],\"someMultiset\":[\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"a\"],"
563  "\"someUnorderedMultiset\":[\"a\",\"a\"]}}",
564  &errors);
565  CPPUNIT_ASSERT_EQUAL(3_st, errors.size());
566  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
567  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors.front().expectedType);
568  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.front().actualType);
569  CPPUNIT_ASSERT_EQUAL("number"s, string(errors.front().member));
570  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors.front().record));
571  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::UnexpectedDuplicate, errors[1].kind);
572  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[1].expectedType);
573  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[1].actualType);
574  CPPUNIT_ASSERT_EQUAL("someSet"s, string(errors[1].member));
575  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[1].record));
576  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::UnexpectedDuplicate, errors[2].kind);
577  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[2].expectedType);
578  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[2].actualType);
579  CPPUNIT_ASSERT_EQUAL("someUnorderedSet"s, string(errors[2].member));
580  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[2].record));
581  errors.clear();
582 
583  NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":1,\"text\":"
584  "\"test\",\"boolean\":false}}",
585  &errors);
586  CPPUNIT_ASSERT_EQUAL(1_st, errors.size());
587  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
588  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors.front().expectedType);
589  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors.front().actualType);
590  CPPUNIT_ASSERT_EQUAL("numbers"s, string(errors.front().member));
591  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors.front().record));
592  errors.clear();
593 
594  NestingObject::fromJson("{\"name\":[],\"testObj\":\"this is not an object\"}", &errors);
595  CPPUNIT_ASSERT_EQUAL(2_st, errors.size());
596  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
597  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.front().expectedType);
598  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors.front().actualType);
599  CPPUNIT_ASSERT_EQUAL("name"s, string(errors.front().member));
600  CPPUNIT_ASSERT_EQUAL("NestingObject"s, string(errors.front().record));
601  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.back().kind);
602  CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors.back().expectedType);
603  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.back().actualType);
604  CPPUNIT_ASSERT_EQUAL("testObj"s, string(errors.back().member));
605  CPPUNIT_ASSERT_EQUAL("NestingObject"s, string(errors.back().record));
606  errors.clear();
607 
608  const NestingArray nestingArray(
609  NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[25,{\"number\":42,\"number2\":3.141592653589793,"
610  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},\"foo\",{\"number\":43,\"number2\":3."
611  "141592653589793,\"numbers\":[1,2,3,4,\"bar\"],\"text\":\"test\",\"boolean\":false}]}",
612  &errors));
613  CPPUNIT_ASSERT_EQUAL(3_st, errors.size());
614  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[0].kind);
615  CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors[0].expectedType);
616  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors[0].actualType);
617  CPPUNIT_ASSERT_EQUAL("testObjects"s, string(errors[0].member));
618  CPPUNIT_ASSERT_EQUAL("NestingArray"s, string(errors[0].record));
619  CPPUNIT_ASSERT_EQUAL(0_st, errors[0].index);
620  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[1].kind);
621  CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors[1].expectedType);
622  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors[1].actualType);
623  CPPUNIT_ASSERT_EQUAL(2_st, errors[1].index);
624  CPPUNIT_ASSERT_EQUAL("testObjects"s, string(errors[1].member));
625  CPPUNIT_ASSERT_EQUAL("NestingArray"s, string(errors[1].record));
626  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[2].kind);
627  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors[2].expectedType);
628  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors[2].actualType);
629  CPPUNIT_ASSERT_EQUAL("numbers"s, string(errors[2].member));
630  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[2].record));
631  CPPUNIT_ASSERT_EQUAL(4_st, errors[2].index);
632  errors.clear();
633 
634  errors.throwOn = JsonDeserializationErrors::ThrowOn::TypeMismatch;
635  CPPUNIT_ASSERT_THROW(NestingObject::fromJson("{\"name\":[],\"testObj\":\"this is not an object\"}", &errors), JsonDeserializationError);
636 }
The JsonReflectorTests class tests RapidJSON wrapper which is used to ease code generation.
void testDeserializePrimitives()
Tests deserializing strings, numbers (int, float, double) and boolean.
void testDeserializeNestedObjects()
Tests deserializing nested objects and arrays.
void testDeserializeSimpleObjects()
Tests deserializing simple objects.
RAPIDJSON_NAMESPACE::StringBuffer toJson(const Type &reflectable)
Serializes the specified reflectable.
Definition: reflector.h:849
void testSerializeNestedObjects()
Tests serializing nested object and arrays.
enum ReflectiveRapidJSON::JsonDeserializationErrors::ThrowOn throwOn
void testSerializeSimpleObjects()
Tests serializing objects.
The JsonDeserializationErrors struct can be passed to fromJson() for error handling.
Type fromJson(const char *json, std::size_t jsonSize, JsonDeserializationErrors *errors=nullptr)
Deserializes the specified JSON to.
Definition: reflector.h:861
const char * currentRecord
The name of the class or struct which is currently being processed.
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.
CPPUNIT_TEST_SUITE_REGISTRATION(JsonReflectorTests)
The JsonDeserializationError struct describes any errors of fromJson() except such caused by invalid ...
void testHandlingTypeMismatch()
Tests whether errors are added on type mismatch and in other cases.
void testHandlingParseError()
Tests whether RAPIDJSON_NAMESPACE::ParseResult is thrown correctly when passing invalid JSON to fromJ...
void testSerializePrimitives()
Tests serializing strings, numbers, arrays and boolean.
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:134
The JsonSerializable class provides the CRTP-base for (de)serializable objects.
Definition: reflector.h:28