Reflection for RapidJSON  0.0.5
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<set<int>>::value, "set not considered an array");
37 static_assert(!JsonReflector::IsArray<multiset<int>>::value, "multiset not considered an array");
38 static_assert(JsonReflector::IsArrayOrSet<set<int>>::value, "set is array or set");
39 static_assert(JsonReflector::IsArrayOrSet<multiset<int>>::value, "multiset is array or set");
40 static_assert(JsonReflector::IsSet<unordered_set<int>>::value, "set");
41 static_assert(JsonReflector::IsMultiSet<unordered_multiset<int>>::value, "multiset");
42 static_assert(!JsonReflector::IsArray<string>::value, "string not mapped to array though it is iteratable");
43 static_assert(JsonReflector::IsMapOrHash<map<string, int>>::value, "map mapped to object");
44 static_assert(JsonReflector::IsMapOrHash<unordered_map<string, int>>::value, "hash mapped to object");
45 static_assert(!JsonReflector::IsMapOrHash<vector<int>>::value, "vector not mapped to object");
46 
48 
49 // define some structs for testing serialization
50 struct TestObject : public JsonSerializable<TestObject> {
51  int number;
52  double number2;
53  vector<int> numbers;
54  string text;
55  bool boolean;
56  map<string, int> someMap;
57  unordered_map<string, bool> someHash;
58  set<string> someSet;
59  multiset<string> someMultiset;
60  unordered_set<string> someUnorderedSet;
61  unordered_multiset<string> someUnorderedMultiset;
62 };
63 
64 struct NestingObject : public JsonSerializable<NestingObject> {
65  string name;
66  TestObject testObj;
67 };
68 
69 struct NestingArray : public JsonSerializable<NestingArray> {
70  string name;
71  vector<TestObject> testObjects;
72 };
73 
74 enum SomeEnum {
75  SomeEnumItem1,
76  SomeEnumItem2,
77  SomeEnumItem3,
78 };
79 
80 enum class SomeEnumClass {
81  Item1,
82  Item2,
83  Item3,
84 };
85 
86 // pretend serialization code for structs has been generated
87 namespace ReflectiveRapidJSON {
88 namespace JsonReflector {
89 
90 template <> inline void push<TestObject>(const TestObject &reflectable, Value::Object &value, Document::AllocatorType &allocator)
91 {
92  push(reflectable.number, "number", value, allocator);
93  push(reflectable.number2, "number2", value, allocator);
94  push(reflectable.numbers, "numbers", value, allocator);
95  push(reflectable.text, "text", value, allocator);
96  push(reflectable.boolean, "boolean", value, allocator);
97  push(reflectable.someMap, "someMap", value, allocator);
98  push(reflectable.someHash, "someHash", value, allocator);
99  push(reflectable.someSet, "someSet", value, allocator);
100  push(reflectable.someMultiset, "someMultiset", value, allocator);
101  push(reflectable.someUnorderedSet, "someUnorderedSet", value, allocator);
102  push(reflectable.someUnorderedMultiset, "someUnorderedMultiset", value, allocator);
103 }
104 
105 template <> inline void push<NestingObject>(const NestingObject &reflectable, Value::Object &value, Document::AllocatorType &allocator)
106 {
107  push(reflectable.name, "name", value, allocator);
108  push(reflectable.testObj, "testObj", value, allocator);
109 }
110 
111 template <> inline void push<NestingArray>(const NestingArray &reflectable, Value::Object &value, Document::AllocatorType &allocator)
112 {
113  push(reflectable.name, "name", value, allocator);
114  push(reflectable.testObjects, "testObjects", value, allocator);
115 }
116 
117 template <>
118 inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
119 {
120  const char *previousRecord;
121  if (errors) {
122  previousRecord = errors->currentRecord;
123  errors->currentRecord = "TestObject";
124  }
125  pull(reflectable.number, "number", value, errors);
126  pull(reflectable.number2, "number2", value, errors);
127  pull(reflectable.numbers, "numbers", value, errors);
128  pull(reflectable.text, "text", value, errors);
129  pull(reflectable.boolean, "boolean", value, errors);
130  pull(reflectable.someMap, "someMap", value, errors);
131  pull(reflectable.someHash, "someHash", value, errors);
132  pull(reflectable.someSet, "someSet", value, errors);
133  pull(reflectable.someMultiset, "someMultiset", value, errors);
134  pull(reflectable.someUnorderedSet, "someUnorderedSet", value, errors);
135  pull(reflectable.someUnorderedMultiset, "someUnorderedMultiset", value, errors);
136  if (errors) {
137  errors->currentRecord = previousRecord;
138  }
139 }
140 
141 template <>
142 inline void pull<NestingObject>(NestingObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
143 {
144  const char *previousRecord;
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 
156 template <>
157 inline void pull<NestingArray>(NestingArray &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
158 {
159  const char *previousRecord;
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 
182 class 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(testDeserializePrimitives);
190  CPPUNIT_TEST(testDeserializeSimpleObjects);
191  CPPUNIT_TEST(testDeserializeNestedObjects);
192  CPPUNIT_TEST(testDeserializeUniquePtr);
193  CPPUNIT_TEST(testDeserializeSharedPtr);
194  CPPUNIT_TEST(testHandlingParseError);
195  CPPUNIT_TEST(testHandlingTypeMismatch);
196  CPPUNIT_TEST_SUITE_END();
197 
198 public:
199  void setUp();
200  void tearDown();
201 
202  void experiment();
203  void testSerializePrimitives();
204  void testSerializeSimpleObjects();
205  void testSerializeNestedObjects();
206  void testSerializeUniquePtr();
207  void testSerializeSharedPtr();
208  void testDeserializePrimitives();
209  void testDeserializeSimpleObjects();
210  void testDeserializeNestedObjects();
211  void testDeserializeUniquePtr();
212  void testDeserializeSharedPtr();
213  void testHandlingParseError();
214  void testHandlingTypeMismatch();
215 
216 private:
217 };
218 
220 
222 {
223 }
224 
226 {
227 }
228 
233 {
234  Document doc(kArrayType);
235  Document::AllocatorType &alloc = doc.GetAllocator();
236  doc.SetArray();
237  Document::Array array(doc.GetArray());
238 
239  // string
240  const string foo("foo"); // musn't be destroyed until JSON is actually written
241  JsonReflector::push<string>(foo, array, alloc);
242  JsonReflector::push<const char *>("bar", array, alloc);
243  // number
244  JsonReflector::push<int>(25, array, alloc);
245  JsonReflector::push<double>(12.5, array, alloc);
246  // enum
247  JsonReflector::push<SomeEnum>(SomeEnumItem2, array, alloc);
248  JsonReflector::push<SomeEnumClass>(SomeEnumClass::Item2, array, alloc);
249  JsonReflector::push<SomeEnumClass>(SomeEnumClass::Item3, array, alloc);
250  // array
251  JsonReflector::push<vector<const char *>>({ "foo1", "bar1" }, array, alloc);
252  JsonReflector::push<list<const char *>>({ "foo2", "bar2" }, array, alloc);
253  JsonReflector::push<initializer_list<const char *>>({ "foo3", "bar3" }, array, alloc);
254  JsonReflector::push<tuple<int, double>>(make_tuple(2, 413.0), array, alloc);
255  // boolean
256  JsonReflector::push<bool>(true, array, alloc);
257  JsonReflector::push<bool>(false, array, alloc);
258 
259  StringBuffer strbuf;
260  Writer<StringBuffer> jsonWriter(strbuf);
261  doc.Accept(jsonWriter);
262  CPPUNIT_ASSERT_EQUAL("[\"foo\",\"bar\",25,12.5,1,1,2,[\"foo1\",\"bar1\"],[\"foo2\",\"bar2\"],[\"foo3\",\"bar3\"],[2,413.0],true,false]"s,
263  string(strbuf.GetString()));
264 }
265 
270 {
271  TestObject testObj;
272  testObj.number = 42;
273  testObj.number2 = 3.141592653589793;
274  testObj.numbers = { 1, 2, 3, 4 };
275  testObj.text = "test";
276  testObj.boolean = false;
277  testObj.someMap = { { "a", 1 }, { "b", 2 } };
278  testObj.someHash = { { "c", true }, { "d", false } };
279  testObj.someSet = { "a", "b", "c" };
280  testObj.someMultiset = { "a", "b", "b" };
281  testObj.someUnorderedSet = { "a" };
282  testObj.someUnorderedMultiset = { "b", "b", "b" };
283  CPPUNIT_ASSERT_EQUAL(
284  "{\"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,
285  string(testObj.toJson().GetString()));
286 }
287 
292 {
293  NestingObject nestingObj;
294  nestingObj.name = "nesting";
295  TestObject &testObj = nestingObj.testObj;
296  testObj.number = 42;
297  testObj.number2 = 3.141592653589793;
298  testObj.numbers = { 1, 2, 3, 4 };
299  testObj.text = "test";
300  testObj.boolean = false;
301  CPPUNIT_ASSERT_EQUAL(
302  "{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}}"s,
303  string(nestingObj.toJson().GetString()));
304 
305  NestingArray nestingArray;
306  nestingArray.name = "nesting2";
307  nestingArray.testObjects.emplace_back(testObj);
308  nestingArray.testObjects.emplace_back(testObj);
309  nestingArray.testObjects.back().number = 43;
310  CPPUNIT_ASSERT_EQUAL(
311  "{\"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,
312  string(nestingArray.toJson().GetString()));
313 
314  vector<TestObject> nestedInVector;
315  nestedInVector.emplace_back(testObj);
316  CPPUNIT_ASSERT_EQUAL(
317  "[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}]"s,
318  string(JsonReflector::toJson(nestedInVector).GetString()));
319 }
320 
322 {
323  Document doc(kArrayType);
324  Document::AllocatorType &alloc = doc.GetAllocator();
325  doc.SetArray();
326  Document::Array array(doc.GetArray());
327 
328  const auto str = make_unique<string>("foo");
329  std::unique_ptr<string> nullStr;
330  const auto obj = make_unique<TestObject>();
331  obj->number = 42;
332  obj->number2 = 3.141592653589793;
333  obj->numbers = { 1, 2, 3, 4 };
334  obj->text = "bar";
335  obj->boolean = false;
336 
337  JsonReflector::push(str, array, alloc);
338  JsonReflector::push(nullStr, array, alloc);
339  JsonReflector::push(obj, array, alloc);
340 
341  StringBuffer strbuf;
342  Writer<StringBuffer> jsonWriter(strbuf);
343  doc.Accept(jsonWriter);
344  CPPUNIT_ASSERT_EQUAL(
345  "[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}]"s,
346  string(strbuf.GetString()));
347 }
348 
350 {
351  Document doc(kArrayType);
352  Document::AllocatorType &alloc = doc.GetAllocator();
353  doc.SetArray();
354  Document::Array array(doc.GetArray());
355 
356  const auto str = make_shared<string>("foo");
357  std::unique_ptr<string> nullStr;
358  const auto obj = make_shared<TestObject>();
359  obj->number = 42;
360  obj->number2 = 3.141592653589793;
361  obj->numbers = { 1, 2, 3, 4 };
362  obj->text = "bar";
363  obj->boolean = false;
364 
365  JsonReflector::push(str, array, alloc);
366  JsonReflector::push(nullStr, array, alloc);
367  JsonReflector::push(obj, array, alloc);
368 
369  StringBuffer strbuf;
370  Writer<StringBuffer> jsonWriter(strbuf);
371  doc.Accept(jsonWriter);
372  CPPUNIT_ASSERT_EQUAL(
373  "[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}]"s,
374  string(strbuf.GetString()));
375 }
376 
381 {
382  Document doc(kArrayType);
383 
384  doc.Parse("[\"a\", 5, 5.0, 5e6, 4, \"test\", true, 4.125, false]");
385  auto array = doc.GetArray().begin();
386 
387  string str1, str2;
388  int int1 = 0, int2 = 0;
389  bool bool1 = false, bool2 = true;
390  float float1 = 0.0f, float2 = 0.0f;
391  double double1 = 0.0;
393  JsonReflector::pull(str1, array, &errors);
394  JsonReflector::pull(int1, array, &errors);
395  JsonReflector::pull(int2, array, &errors);
396  JsonReflector::pull(float1, array, &errors);
397  JsonReflector::pull(float2, array, &errors);
398  JsonReflector::pull(str2, array, &errors);
399  JsonReflector::pull(bool1, array, &errors);
400  JsonReflector::pull(double1, array, &errors);
401  JsonReflector::pull(bool2, array, &errors);
402 
403  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
404  CPPUNIT_ASSERT_EQUAL("a"s, str1);
405  CPPUNIT_ASSERT_EQUAL(5, int1);
406  CPPUNIT_ASSERT_EQUAL(5, int2);
407  CPPUNIT_ASSERT_EQUAL(5e6f, float1);
408  CPPUNIT_ASSERT_EQUAL(4.f, float2);
409  CPPUNIT_ASSERT_EQUAL("test"s, str2);
410  CPPUNIT_ASSERT_EQUAL(true, bool1);
411  CPPUNIT_ASSERT_EQUAL(4.125, double1);
412  CPPUNIT_ASSERT_EQUAL(false, bool2);
413 
414  // deserialize primitives as tuple
415  tuple<string, int, int, float, float, string, bool, double, bool> arrayAsTuple;
416  JsonReflector::pull(arrayAsTuple, doc, &errors);
417  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
418  CPPUNIT_ASSERT_EQUAL("a"s, get<0>(arrayAsTuple));
419  CPPUNIT_ASSERT_EQUAL(5, get<1>(arrayAsTuple));
420  CPPUNIT_ASSERT_EQUAL(5, get<2>(arrayAsTuple));
421  CPPUNIT_ASSERT_EQUAL(5e6f, get<3>(arrayAsTuple));
422  CPPUNIT_ASSERT_EQUAL(4.f, get<4>(arrayAsTuple));
423  CPPUNIT_ASSERT_EQUAL("test"s, get<5>(arrayAsTuple));
424  CPPUNIT_ASSERT_EQUAL(true, get<6>(arrayAsTuple));
425  CPPUNIT_ASSERT_EQUAL(4.125, get<7>(arrayAsTuple));
426  CPPUNIT_ASSERT_EQUAL(false, get<8>(arrayAsTuple));
427  tuple<string, int> anotherTuple;
428  JsonReflector::pull(anotherTuple, doc, &errors);
429  CPPUNIT_ASSERT_EQUAL(1_st, errors.size());
430  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::ArraySizeMismatch, errors.front().kind);
431 }
432 
437 {
438  const TestObject testObj(
439  TestObject::fromJson("{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":"
440  "false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"c\":true,\"d\":false},\"someSet\":[\"a\",\"b\"],\"someMultiset\":["
441  "\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"b\"],\"someUnorderedMultiset\":[\"a\",\"a\"]}"));
442 
443  CPPUNIT_ASSERT_EQUAL(42, testObj.number);
444  CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
445  CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
446  CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
447  CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
448  const map<string, int> expectedMap{ { "a", 1 }, { "b", 2 } };
449  CPPUNIT_ASSERT_EQUAL(expectedMap, testObj.someMap);
450  const unordered_map<string, bool> expectedHash{ { "c", true }, { "d", false } };
451  CPPUNIT_ASSERT_EQUAL(expectedHash, testObj.someHash);
452  CPPUNIT_ASSERT_EQUAL(set<string>({ "a", "b" }), testObj.someSet);
453  CPPUNIT_ASSERT_EQUAL(multiset<string>({ "a", "a" }), testObj.someMultiset);
454  CPPUNIT_ASSERT_EQUAL(unordered_set<string>({ "a", "b" }), testObj.someUnorderedSet);
455  CPPUNIT_ASSERT_EQUAL(unordered_multiset<string>({ "a", "a" }), testObj.someUnorderedMultiset);
456 }
457 
462 {
464  const NestingObject nestingObj(NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,"
465  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}}",
466  &errors));
467  const TestObject &testObj = nestingObj.testObj;
468  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
469  CPPUNIT_ASSERT_EQUAL("nesting"s, nestingObj.name);
470  CPPUNIT_ASSERT_EQUAL(42, testObj.number);
471  CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
472  CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
473  CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
474  CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
475 
476  const NestingArray nestingArray(NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,"
477  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},{\"number\":43,\"number2\":3."
478  "141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}]}",
479  &errors));
480  const vector<TestObject> &testObjects = nestingArray.testObjects;
481  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
482  CPPUNIT_ASSERT_EQUAL("nesting2"s, nestingArray.name);
483  CPPUNIT_ASSERT_EQUAL(2_st, testObjects.size());
484  CPPUNIT_ASSERT_EQUAL(42, testObjects[0].number);
485  CPPUNIT_ASSERT_EQUAL(43, testObjects[1].number);
486  for (const TestObject &testObj : testObjects) {
487  CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
488  CPPUNIT_ASSERT_EQUAL(vector<int>({ 1, 2, 3, 4 }), testObj.numbers);
489  CPPUNIT_ASSERT_EQUAL("test"s, testObj.text);
490  CPPUNIT_ASSERT_EQUAL(false, testObj.boolean);
491  }
492 
493  const auto nestedInVector(JsonReflector::fromJson<vector<TestObject>>(
494  "[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]",
495  &errors));
496  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
497  CPPUNIT_ASSERT_EQUAL(1_st, nestedInVector.size());
498  CPPUNIT_ASSERT_EQUAL(42, nestedInVector[0].number);
499  CPPUNIT_ASSERT_EQUAL(4_st, nestedInVector[0].numbers.size());
500  CPPUNIT_ASSERT_EQUAL("test"s, nestedInVector[0].text);
501 }
502 
504 {
505  Document doc(kArrayType);
506  doc.Parse("[\"foo\",null,{\"text\":\"bar\"}]");
507  auto array = doc.GetArray().begin();
508 
509  unique_ptr<string> str;
510  unique_ptr<string> nullStr;
511  unique_ptr<TestObject> obj;
513  JsonReflector::pull(str, array, &errors);
514  JsonReflector::pull(nullStr, array, &errors);
515  JsonReflector::pull(obj, array, &errors);
516 
517  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
518  CPPUNIT_ASSERT(str);
519  CPPUNIT_ASSERT_EQUAL("foo"s, *str);
520  CPPUNIT_ASSERT(!nullStr);
521  CPPUNIT_ASSERT(obj);
522  CPPUNIT_ASSERT_EQUAL("bar"s, obj->text);
523 }
524 
526 {
527  Document doc(kArrayType);
528  doc.Parse("[\"foo\",null,{\"text\":\"bar\"}]");
529  auto array = doc.GetArray().begin();
530 
531  shared_ptr<string> str;
532  shared_ptr<string> nullStr;
533  shared_ptr<TestObject> obj;
535  JsonReflector::pull(str, array, &errors);
536  JsonReflector::pull(nullStr, array, &errors);
537  JsonReflector::pull(obj, array, &errors);
538 
539  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
540  CPPUNIT_ASSERT(str);
541  CPPUNIT_ASSERT_EQUAL("foo"s, *str);
542  CPPUNIT_ASSERT(!nullStr);
543  CPPUNIT_ASSERT(obj);
544  CPPUNIT_ASSERT_EQUAL("bar"s, obj->text);
545 }
546 
551 {
552  try {
553  NestingObject::fromJson("{\"name\":nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
554  "\"test\",\"boolean\":false}}");
555  CPPUNIT_FAIL("expected ParseResult thrown");
556  } catch (const RAPIDJSON_NAMESPACE::ParseResult &res) {
557  CPPUNIT_ASSERT_EQUAL(RAPIDJSON_NAMESPACE::kParseErrorValueInvalid, res.Code());
558  CPPUNIT_ASSERT_EQUAL(9_st, res.Offset());
559  }
560 }
561 
566 {
568  NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,"
569  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},{\"number\":43,\"number2\":3."
570  "141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false}]}",
571  &errors);
572  CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
573 
574  NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":\"42\",\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
575  "\"test\",\"boolean\":false,\"someSet\":[\"a\",\"a\"],\"someMultiset\":[\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"a\"],"
576  "\"someUnorderedMultiset\":[\"a\",\"a\"]}}",
577  &errors);
578  CPPUNIT_ASSERT_EQUAL(3_st, errors.size());
579  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
580  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors.front().expectedType);
581  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.front().actualType);
582  CPPUNIT_ASSERT_EQUAL("number"s, string(errors.front().member));
583  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors.front().record));
584  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::UnexpectedDuplicate, errors[1].kind);
585  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[1].expectedType);
586  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[1].actualType);
587  CPPUNIT_ASSERT_EQUAL("someSet"s, string(errors[1].member));
588  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[1].record));
589  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::UnexpectedDuplicate, errors[2].kind);
590  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[2].expectedType);
591  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[2].actualType);
592  CPPUNIT_ASSERT_EQUAL("someUnorderedSet"s, string(errors[2].member));
593  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[2].record));
594  errors.clear();
595 
596  NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":1,\"text\":"
597  "\"test\",\"boolean\":false}}",
598  &errors);
599  CPPUNIT_ASSERT_EQUAL(1_st, errors.size());
600  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
601  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors.front().expectedType);
602  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors.front().actualType);
603  CPPUNIT_ASSERT_EQUAL("numbers"s, string(errors.front().member));
604  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors.front().record));
605  errors.clear();
606 
607  NestingObject::fromJson("{\"name\":[],\"testObj\":\"this is not an object\"}", &errors);
608  CPPUNIT_ASSERT_EQUAL(2_st, errors.size());
609  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
610  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.front().expectedType);
611  CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors.front().actualType);
612  CPPUNIT_ASSERT_EQUAL("name"s, string(errors.front().member));
613  CPPUNIT_ASSERT_EQUAL("NestingObject"s, string(errors.front().record));
614  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.back().kind);
615  CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors.back().expectedType);
616  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.back().actualType);
617  CPPUNIT_ASSERT_EQUAL("testObj"s, string(errors.back().member));
618  CPPUNIT_ASSERT_EQUAL("NestingObject"s, string(errors.back().record));
619  errors.clear();
620 
621  const NestingArray nestingArray(
622  NestingArray::fromJson("{\"name\":\"nesting2\",\"testObjects\":[25,{\"number\":42,\"number2\":3.141592653589793,"
623  "\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false},\"foo\",{\"number\":43,\"number2\":3."
624  "141592653589793,\"numbers\":[1,2,3,4,\"bar\"],\"text\":\"test\",\"boolean\":false}]}",
625  &errors));
626  CPPUNIT_ASSERT_EQUAL(3_st, errors.size());
627  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[0].kind);
628  CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors[0].expectedType);
629  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors[0].actualType);
630  CPPUNIT_ASSERT_EQUAL("testObjects"s, string(errors[0].member));
631  CPPUNIT_ASSERT_EQUAL("NestingArray"s, string(errors[0].record));
632  CPPUNIT_ASSERT_EQUAL(0_st, errors[0].index);
633  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[1].kind);
634  CPPUNIT_ASSERT_EQUAL(JsonType::Object, errors[1].expectedType);
635  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors[1].actualType);
636  CPPUNIT_ASSERT_EQUAL(2_st, errors[1].index);
637  CPPUNIT_ASSERT_EQUAL("testObjects"s, string(errors[1].member));
638  CPPUNIT_ASSERT_EQUAL("NestingArray"s, string(errors[1].record));
639  CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors[2].kind);
640  CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors[2].expectedType);
641  CPPUNIT_ASSERT_EQUAL(JsonType::String, errors[2].actualType);
642  CPPUNIT_ASSERT_EQUAL("numbers"s, string(errors[2].member));
643  CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[2].record));
644  CPPUNIT_ASSERT_EQUAL(4_st, errors[2].index);
645  errors.clear();
646 
647  errors.throwOn = JsonDeserializationErrors::ThrowOn::TypeMismatch;
648  CPPUNIT_ASSERT_THROW(NestingObject::fromJson("{\"name\":[],\"testObj\":\"this is not an object\"}", &errors), JsonDeserializationError);
649 }
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.
Traits::Any< Traits::IsSpecializationOf< Type, std::set >, Traits::IsSpecializationOf< Type, std::unordered_set > > IsSet
Definition: reflector.h:95
STL namespace.
Traits::Any< Traits::IsSpecializationOf< Type, std::multiset >, Traits::IsSpecializationOf< Type, std::unordered_multiset > > IsMultiSet
Definition: reflector.h:97
void testDeserializeSimpleObjects()
Tests deserializing simple objects.
Traits::Any< Traits::IsSpecializationOf< Type, std::map >, Traits::IsSpecializationOf< Type, std::unordered_map > > IsMapOrHash
Definition: reflector.h:94
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:782
void testSerializeNestedObjects()
Tests serializing nested object and arrays.
Traits::All< Traits::IsIteratable< Type >, Traits::Not< Traits::IsSpecializationOf< Type, std::basic_string > >, Traits::Not< IsMapOrHash< Type > >, Traits::Not< IsSet< Type > >, Traits::Not< IsMultiSet< Type > >> IsArray
Definition: reflector.h:103
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:838
Traits::All< Traits::IsIteratable< Type >, Traits::Not< Traits::IsSpecializationOf< Type, std::basic_string > >, Traits::Not< IsMapOrHash< Type > >> IsArrayOrSet
Definition: reflector.h:100
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:150
The JsonSerializable class provides the CRTP-base for (de)serializable objects.
Definition: reflector.h:31