Reflection for RapidJSON  0.0.2
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 
33 // test traits
34 static_assert(JsonReflector::IsArray<vector<int>>::value, "vector mapped to array");
35 static_assert(JsonReflector::IsArray<list<int>>::value, "list mapped to array");
36 static_assert(!JsonReflector::IsArray<string>::value, "string mapped to string");
37 static_assert(JsonReflector::IsMapOrHash<map<string, int>>::value, "map mapped to object");
38 static_assert(JsonReflector::IsMapOrHash<unordered_map<string, int>>::value, "hash mapped to object");
39 static_assert(!JsonReflector::IsMapOrHash<vector<int>>::value, "vector not mapped to object");
40 
42 
43 // define some structs for testing serialization
44 struct TestObject : public JsonSerializable<TestObject> {
45  int number;
46  double number2;
47  vector<int> numbers;
48  string text;
49  bool boolean;
50  map<string, int> someMap;
51  unordered_map<string, bool> someHash;
52 };
53 
54 struct NestingObject : public JsonSerializable<NestingObject> {
55  string name;
56  TestObject testObj;
57 };
58 
59 struct NestingArray : public JsonSerializable<NestingArray> {
60  string name;
61  vector<TestObject> testObjects;
62 };
63 
64 enum SomeEnum {
65  SomeEnumItem1,
66  SomeEnumItem2,
67  SomeEnumItem3,
68 };
69 
70 enum class SomeEnumClass {
71  Item1,
72  Item2,
73  Item3,
74 };
75 
76 // pretend serialization code for structs has been generated
77 namespace ReflectiveRapidJSON {
78 namespace JsonReflector {
79 
80 template <> 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 }
90 
91 template <> inline void push<NestingObject>(const NestingObject &reflectable, Value::Object &value, Document::AllocatorType &allocator)
92 {
93  push(reflectable.name, "name", value, allocator);
94  push(reflectable.testObj, "testObj", value, allocator);
95 }
96 
97 template <> inline void push<NestingArray>(const NestingArray &reflectable, Value::Object &value, Document::AllocatorType &allocator)
98 {
99  push(reflectable.name, "name", value, allocator);
100  push(reflectable.testObjects, "testObjects", value, allocator);
101 }
102 
103 template <>
104 inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
105 {
106  const char *previousRecord;
107  if (errors) {
108  previousRecord = errors->currentRecord;
109  errors->currentRecord = "TestObject";
110  }
111  pull(reflectable.number, "number", value, errors);
112  pull(reflectable.number2, "number2", value, errors);
113  pull(reflectable.numbers, "numbers", value, errors);
114  pull(reflectable.text, "text", value, errors);
115  pull(reflectable.boolean, "boolean", value, errors);
116  pull(reflectable.someMap, "someMap", value, errors);
117  pull(reflectable.someHash, "someHash", value, errors);
118  if (errors) {
119  errors->currentRecord = previousRecord;
120  }
121 }
122 
123 template <>
124 inline void pull<NestingObject>(NestingObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
125 {
126  const char *previousRecord;
127  if (errors) {
128  previousRecord = errors->currentRecord;
129  errors->currentRecord = "NestingObject";
130  }
131  pull(reflectable.name, "name", value, errors);
132  pull(reflectable.testObj, "testObj", value, errors);
133  if (errors) {
134  errors->currentRecord = previousRecord;
135  }
136 }
137 
138 template <>
139 inline void pull<NestingArray>(NestingArray &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
140 {
141  const char *previousRecord;
142  if (errors) {
143  previousRecord = errors->currentRecord;
144  errors->currentRecord = "NestingArray";
145  }
146  pull(reflectable.name, "name", value, errors);
147  pull(reflectable.testObjects, "testObjects", value, errors);
148  if (errors) {
149  errors->currentRecord = previousRecord;
150  }
151 }
152 
153 } // namespace JsonReflector
154 
155 // namespace JsonReflector
156 } // namespace ReflectiveRapidJSON
157 
159 
164 class JsonReflectorTests : public TestFixture {
165  CPPUNIT_TEST_SUITE(JsonReflectorTests);
166  CPPUNIT_TEST(testSerializePrimitives);
167  CPPUNIT_TEST(testSerializeSimpleObjects);
168  CPPUNIT_TEST(testSerializeNestedObjects);
169  CPPUNIT_TEST(testSerializeUniquePtr);
170  CPPUNIT_TEST(testSerializeSharedPtr);
171  CPPUNIT_TEST(testDeserializePrimitives);
172  CPPUNIT_TEST(testDeserializeSimpleObjects);
173  CPPUNIT_TEST(testDeserializeNestedObjects);
174  CPPUNIT_TEST(testDeserializeUniquePtr);
175  CPPUNIT_TEST(testDeserializeSharedPtr);
176  CPPUNIT_TEST(testHandlingParseError);
177  CPPUNIT_TEST(testHandlingTypeMismatch);
178  CPPUNIT_TEST_SUITE_END();
179 
180 public:
181  void setUp();
182  void tearDown();
183 
184  void experiment();
185  void testSerializePrimitives();
186  void testSerializeSimpleObjects();
187  void testSerializeNestedObjects();
188  void testSerializeUniquePtr();
189  void testSerializeSharedPtr();
190  void testDeserializePrimitives();
191  void testDeserializeSimpleObjects();
192  void testDeserializeNestedObjects();
193  void testDeserializeUniquePtr();
194  void testDeserializeSharedPtr();
195  void testHandlingParseError();
196  void testHandlingTypeMismatch();
197 
198 private:
199 };
200 
202 
204 {
205 }
206 
208 {
209 }
210 
215 {
216  Document doc(kArrayType);
217  Document::AllocatorType &alloc = doc.GetAllocator();
218  doc.SetArray();
219  Document::Array array(doc.GetArray());
220 
221  // string
222  const string foo("foo"); // musn't be destroyed until JSON is actually written
223  JsonReflector::push<string>(foo, array, alloc);
224  JsonReflector::push<const char *>("bar", array, alloc);
225  // number
226  JsonReflector::push<int>(25, array, alloc);
227  JsonReflector::push<double>(12.5, array, alloc);
228  // enum
229  JsonReflector::push<SomeEnum>(SomeEnumItem2, array, alloc);
230  JsonReflector::push<SomeEnumClass>(SomeEnumClass::Item2, array, alloc);
231  JsonReflector::push<SomeEnumClass>(SomeEnumClass::Item3, array, alloc);
232  // array
233  JsonReflector::push<vector<const char *>>({ "foo1", "bar1" }, array, alloc);
234  JsonReflector::push<list<const char *>>({ "foo2", "bar2" }, array, alloc);
235  JsonReflector::push<initializer_list<const char *>>({ "foo3", "bar3" }, array, alloc);
236  JsonReflector::push<tuple<int, double>>(make_tuple(2, 413.0), array, alloc);
237  // boolean
238  JsonReflector::push<bool>(true, array, alloc);
239  JsonReflector::push<bool>(false, array, alloc);
240 
241  StringBuffer strbuf;
242  Writer<StringBuffer> jsonWriter(strbuf);
243  doc.Accept(jsonWriter);
244  CPPUNIT_ASSERT_EQUAL("[\"foo\",\"bar\",25,12.5,1,1,2,[\"foo1\",\"bar1\"],[\"foo2\",\"bar2\"],[\"foo3\",\"bar3\"],[2,413.0],true,false]"s,
245  string(strbuf.GetString()));
246 }
247 
252 {
253  TestObject testObj;
254  testObj.number = 42;
255  testObj.number2 = 3.141592653589793;
256  testObj.numbers = { 1, 2, 3, 4 };
257  testObj.text = "test";
258  testObj.boolean = false;
259  testObj.someMap = { { "a", 1 }, { "b", 2 } };
260  testObj.someHash = { { "c", true }, { "d", false } };
261  CPPUNIT_ASSERT_EQUAL(
262  "{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"d\":false,\"c\":true}}"s,
263  string(testObj.toJson().GetString()));
264 }
265 
270 {
271  NestingObject nestingObj;
272  nestingObj.name = "nesting";
273  TestObject &testObj = nestingObj.testObj;
274  testObj.number = 42;
275  testObj.number2 = 3.141592653589793;
276  testObj.numbers = { 1, 2, 3, 4 };
277  testObj.text = "test";
278  testObj.boolean = false;
279  CPPUNIT_ASSERT_EQUAL(
280  "{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}}"s,
281  string(nestingObj.toJson().GetString()));
282 
283  NestingArray nestingArray;
284  nestingArray.name = "nesting2";
285  nestingArray.testObjects.emplace_back(testObj);
286  nestingArray.testObjects.emplace_back(testObj);
287  nestingArray.testObjects.back().number = 43;
288  CPPUNIT_ASSERT_EQUAL(
289  "{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{}},{\"number\":43,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]}"s,
290  string(nestingArray.toJson().GetString()));
291 
292  vector<TestObject> nestedInVector;
293  nestedInVector.emplace_back(testObj);
294  CPPUNIT_ASSERT_EQUAL(
295  "[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]"s,
296  string(JsonReflector::toJson(nestedInVector).GetString()));
297 }
298 
300 {
301  Document doc(kArrayType);
302  Document::AllocatorType &alloc = doc.GetAllocator();
303  doc.SetArray();
304  Document::Array array(doc.GetArray());
305 
306  const auto str = make_unique<string>("foo");
307  std::unique_ptr<string> nullStr;
308  const auto obj = make_unique<TestObject>();
309  obj->number = 42;
310  obj->number2 = 3.141592653589793;
311  obj->numbers = { 1, 2, 3, 4 };
312  obj->text = "bar";
313  obj->boolean = false;
314 
315  JsonReflector::push(str, array, alloc);
316  JsonReflector::push(nullStr, array, alloc);
317  JsonReflector::push(obj, array, alloc);
318 
319  StringBuffer strbuf;
320  Writer<StringBuffer> jsonWriter(strbuf);
321  doc.Accept(jsonWriter);
322  CPPUNIT_ASSERT_EQUAL(
323  "[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]"s,
324  string(strbuf.GetString()));
325 }
326 
328 {
329  Document doc(kArrayType);
330  Document::AllocatorType &alloc = doc.GetAllocator();
331  doc.SetArray();
332  Document::Array array(doc.GetArray());
333 
334  const auto str = make_shared<string>("foo");
335  std::unique_ptr<string> nullStr;
336  const auto obj = make_shared<TestObject>();
337  obj->number = 42;
338  obj->number2 = 3.141592653589793;
339  obj->numbers = { 1, 2, 3, 4 };
340  obj->text = "bar";
341  obj->boolean = false;
342 
343  JsonReflector::push(str, array, alloc);
344  JsonReflector::push(nullStr, array, alloc);
345  JsonReflector::push(obj, array, alloc);
346 
347  StringBuffer strbuf;
348  Writer<StringBuffer> jsonWriter(strbuf);
349  doc.Accept(jsonWriter);
350  CPPUNIT_ASSERT_EQUAL(
351  "[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]"s,
352  string(strbuf.GetString()));
353 }
354 
359 {
360  Document doc(kArrayType);
361 
362  doc.Parse("[\"a\", 5, 5.0, 5e6, 4, \"test\", true, 4.125, false]");
363  auto array = doc.GetArray().begin();
364 
365  string str1, str2;
366  int int1 = 0, int2 = 0;
367  bool bool1 = false, bool2 = true;
368  float float1 = 0.0f, float2 = 0.0f;
369  double double1 = 0.0;
371  JsonReflector::pull(str1, array, &errors);
372  JsonReflector::pull(int1, array, &errors);
373  JsonReflector::pull(int2, array, &errors);
374  JsonReflector::pull(float1, array, &errors);
375  JsonReflector::pull(float2, array, &errors);
376  JsonReflector::pull(str2, array, &errors);
377  JsonReflector::pull(bool1, array, &errors);
378  JsonReflector::pull(double1, array, &errors);
379  JsonReflector::pull(bool2, array, &errors);
380 
381  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
382  CPPUNIT_ASSERT_EQUAL("a"s, str1);
383  CPPUNIT_ASSERT_EQUAL(5, int1);
384  CPPUNIT_ASSERT_EQUAL(5, int2);
385  CPPUNIT_ASSERT_EQUAL(5e6f, float1);
386  CPPUNIT_ASSERT_EQUAL(4.f, float2);
387  CPPUNIT_ASSERT_EQUAL("test"s, str2);
388  CPPUNIT_ASSERT_EQUAL(true, bool1);
389  CPPUNIT_ASSERT_EQUAL(4.125, double1);
390  CPPUNIT_ASSERT_EQUAL(false, bool2);
391 
392  // deserialize primitives as tuple
393  tuple<string, int, int, float, float, string, bool, double, bool> arrayAsTuple;
394  JsonReflector::pull(arrayAsTuple, doc, &errors);
395  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
396  CPPUNIT_ASSERT_EQUAL("a"s, get<0>(arrayAsTuple));
397  CPPUNIT_ASSERT_EQUAL(5, get<1>(arrayAsTuple));
398  CPPUNIT_ASSERT_EQUAL(5, get<2>(arrayAsTuple));
399  CPPUNIT_ASSERT_EQUAL(5e6f, get<3>(arrayAsTuple));
400  CPPUNIT_ASSERT_EQUAL(4.f, get<4>(arrayAsTuple));
401  CPPUNIT_ASSERT_EQUAL("test"s, get<5>(arrayAsTuple));
402  CPPUNIT_ASSERT_EQUAL(true, get<6>(arrayAsTuple));
403  CPPUNIT_ASSERT_EQUAL(4.125, get<7>(arrayAsTuple));
404  CPPUNIT_ASSERT_EQUAL(false, get<8>(arrayAsTuple));
405  tuple<string, int> anotherTuple;
406  JsonReflector::pull(anotherTuple, doc, &errors);
407  CPPUNIT_ASSERT_EQUAL(1_st, errors.size());
408  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::ArraySizeMismatch, errors.front().kind);
409 }
410 
415 {
416  const TestObject testObj(TestObject::fromJson("{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":"
417  "false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"c\":true,\"d\":false}}"));
418 
419  CPPUNIT_ASSERT_EQUAL(42, testObj.number);
420  CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
421  CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
422  CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
423  CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
424  const map<string, int> expectedMap{ { "a", 1 }, { "b", 2 } };
425  CPPUNIT_ASSERT_EQUAL(expectedMap, testObj.someMap);
426  const unordered_map<string, bool> expectedHash{ { "c", true }, { "d", false } };
427  CPPUNIT_ASSERT_EQUAL(expectedHash, testObj.someHash);
428 }
429 
434 {
436  const NestingObject nestingObj(NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,"
437  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}}",
438  &errors));
439  const TestObject &testObj = nestingObj.testObj;
440  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
441  CPPUNIT_ASSERT_EQUAL("nesting"s, nestingObj.name);
442  CPPUNIT_ASSERT_EQUAL(42, testObj.number);
443  CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
444  CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
445  CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
446  CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
447 
448  const NestingArray nestingArray(NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,"
449  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},{\"number\":43,\"number2\":3."
450  "141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}]}",
451  &errors));
452  const vector<TestObject> &testObjects = nestingArray.testObjects;
453  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
454  CPPUNIT_ASSERT_EQUAL("nesting2"s, nestingArray.name);
455  CPPUNIT_ASSERT_EQUAL(2_st, testObjects.size());
456  CPPUNIT_ASSERT_EQUAL(42, testObjects[0].number);
457  CPPUNIT_ASSERT_EQUAL(43, testObjects[1].number);
458  for (const TestObject &testObj : testObjects) {
459  CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
460  CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
461  CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
462  CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
463  }
464 
465  const auto nestedInVector(JsonReflector::fromJson<vector<TestObject>>(
466  "[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]",
467  &errors));
468  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
469  CPPUNIT_ASSERT_EQUAL(1_st, nestedInVector.size());
470  CPPUNIT_ASSERT_EQUAL(42, nestedInVector[0].number);
471  CPPUNIT_ASSERT_EQUAL(4_st, nestedInVector[0].numbers.size());
472  CPPUNIT_ASSERT_EQUAL("test"s, nestedInVector[0].text);
473 }
474 
476 {
477  Document doc(kArrayType);
478  doc.Parse("[\"foo\",null,{\"text\":\"bar\"}]");
479  auto array = doc.GetArray().begin();
480 
481  unique_ptr<string> str;
482  unique_ptr<string> nullStr;
483  unique_ptr<TestObject> obj;
485  JsonReflector::pull(str, array, &errors);
486  JsonReflector::pull(nullStr, array, &errors);
487  JsonReflector::pull(obj, array, &errors);
488 
489  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
490  CPPUNIT_ASSERT(str);
491  CPPUNIT_ASSERT_EQUAL("foo"s, *str);
492  CPPUNIT_ASSERT(!nullStr);
493  CPPUNIT_ASSERT(obj);
494  CPPUNIT_ASSERT_EQUAL("bar"s, obj->text);
495 }
496 
498 {
499  Document doc(kArrayType);
500  doc.Parse("[\"foo\",null,{\"text\":\"bar\"}]");
501  auto array = doc.GetArray().begin();
502 
503  shared_ptr<string> str;
504  shared_ptr<string> nullStr;
505  shared_ptr<TestObject> obj;
507  JsonReflector::pull(str, array, &errors);
508  JsonReflector::pull(nullStr, array, &errors);
509  JsonReflector::pull(obj, array, &errors);
510 
511  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
512  CPPUNIT_ASSERT(str);
513  CPPUNIT_ASSERT_EQUAL("foo"s, *str);
514  CPPUNIT_ASSERT(!nullStr);
515  CPPUNIT_ASSERT(obj);
516  CPPUNIT_ASSERT_EQUAL("bar"s, obj->text);
517 }
518 
523 {
524  try {
525  NestingObject::fromJson("{\"name\":nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
526  "\"test\",\"boolean\":false}}");
527  CPPUNIT_FAIL("expected ParseResult thrown");
528  } catch (const RAPIDJSON_NAMESPACE::ParseResult &res) {
529  CPPUNIT_ASSERT_EQUAL(RAPIDJSON_NAMESPACE::kParseErrorValueInvalid, res.Code());
530  CPPUNIT_ASSERT_EQUAL(9_st, res.Offset());
531  }
532 }
533 
538 {
540  NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,"
541  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},{\"number\":43,\"number2\":3."
542  "141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}]}",
543  &errors);
544  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
545 
546  NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":\"42\",\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
547  "\"test\",\"boolean\":false}}",
548  &errors);
549  CPPUNIT_ASSERT_EQUAL(1_st, errors.size());
550  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
551  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors.front().expectedType);
552  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.front().actualType);
553  CPPUNIT_ASSERT_EQUAL("number"s, string(errors.front().member));
554  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors.front().record));
555  errors.clear();
556 
557  NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":1,\"text\":"
558  "\"test\",\"boolean\":false}}",
559  &errors);
560  CPPUNIT_ASSERT_EQUAL(1_st, errors.size());
561  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
562  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors.front().expectedType);
563  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors.front().actualType);
564  CPPUNIT_ASSERT_EQUAL("numbers"s, string(errors.front().member));
565  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors.front().record));
566  errors.clear();
567 
568  NestingObject::fromJson("{\"name\":[],\"testObj\":\"this is not an object\"}", &errors);
569  CPPUNIT_ASSERT_EQUAL(2_st, errors.size());
570  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
571  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.front().expectedType);
572  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors.front().actualType);
573  CPPUNIT_ASSERT_EQUAL("name"s, string(errors.front().member));
574  CPPUNIT_ASSERT_EQUAL("NestingObject"s, string(errors.front().record));
575  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.back().kind);
576  CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors.back().expectedType);
577  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.back().actualType);
578  CPPUNIT_ASSERT_EQUAL("testObj"s, string(errors.back().member));
579  CPPUNIT_ASSERT_EQUAL("NestingObject"s, string(errors.back().record));
580  errors.clear();
581 
582  const NestingArray nestingArray(
583  NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[25,{\"number\":42,\"number2\":3.141592653589793,"
584  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},\"foo\",{\"number\":43,\"number2\":3."
585  "141592653589793,\"numbers\":[1,2,3,4,\"bar\"],\"text\":\"test\",\"boolean\":false}]}",
586  &errors));
587  CPPUNIT_ASSERT_EQUAL(3_st, errors.size());
588  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[0].kind);
589  CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors[0].expectedType);
590  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors[0].actualType);
591  CPPUNIT_ASSERT_EQUAL("testObjects"s, string(errors[0].member));
592  CPPUNIT_ASSERT_EQUAL("NestingArray"s, string(errors[0].record));
593  CPPUNIT_ASSERT_EQUAL(0_st, errors[0].index);
594  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[1].kind);
595  CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors[1].expectedType);
596  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors[1].actualType);
597  CPPUNIT_ASSERT_EQUAL(2_st, errors[1].index);
598  CPPUNIT_ASSERT_EQUAL("testObjects"s, string(errors[1].member));
599  CPPUNIT_ASSERT_EQUAL("NestingArray"s, string(errors[1].record));
600  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[2].kind);
601  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors[2].expectedType);
602  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors[2].actualType);
603  CPPUNIT_ASSERT_EQUAL("numbers"s, string(errors[2].member));
604  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[2].record));
605  CPPUNIT_ASSERT_EQUAL(4_st, errors[2].index);
606  errors.clear();
607 
608  errors.throwOn = JsonDeserializationErrors::ThrowOn::TypeMismatch;
609  CPPUNIT_ASSERT_THROW(NestingObject::fromJson("{\"name\":[],\"testObj\":\"this is not an object\"}", &errors), JsonDeserializationError);
610 }
Traits::All< Traits::IsIteratable< Type >, Traits::Not< Traits::IsSpecializationOf< Type, std::basic_string > >, Traits::Not< IsMapOrHash< Type > >> IsArray
Definition: reflector.h:95
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.
STL namespace.
void testDeserializeSimpleObjects()
Tests deserializing simple objects.
Traits::Any< Traits::IsSpecializationOf< Type, std::map >, Traits::IsSpecializationOf< Type, std::unordered_map > > IsMapOrHash
Definition: reflector.h:92
RAPIDJSON_NAMESPACE::StringBuffer toJson(const Type &reflectable)
Serializes the specified reflectable which has a custom type or can be mapped to and object...
Definition: reflector.h:704
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:760
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 JsonDeserializationError is thrown on type mismatch.
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:142
The JsonSerializable class provides the CRTP-base for (de)serializable objects.
Definition: reflector.h:29