Support std::(unordered_)?(multi)?set

This commit is contained in:
Martchus 2018-02-26 22:37:43 +01:00
parent 432f997d16
commit 03e3a4bc67
5 changed files with 169 additions and 38 deletions

View File

@ -10,7 +10,7 @@ set(META_APP_CATEGORIES "Utility;")
set(META_GUI_OPTIONAL false)
set(META_VERSION_MAJOR 0)
set(META_VERSION_MINOR 0)
set(META_VERSION_PATCH 3)
set(META_VERSION_PATCH 4)
set(META_APP_VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH})
# set project name for IDEs like Qt Creator

View File

@ -44,20 +44,21 @@ For a full list of further ideas, see [TODOs.md](./TODOs.md).
## Supported datatypes
The following table shows the mapping of supported C++ types to supported JSON types:
| C++ type | JSON type |
| ------------------------------------------------- |:------------:|
| custom structures/classes | object |
| `bool` | true/false |
| signed and unsigned integral types | number |
| `float` and `double` | number |
| `enum` and `enum class` | number |
| `std::string` | string |
| `const char *` | string |
| iteratable lists (`std::vector`, `std::list`, ...)| array |
| `std::tuple` | array |
| `std::unique_ptr`, `std::shared_ptr` | depends/null |
| `std::map`, `std::unordered_map` | object |
| `JsonSerializable` | object |
| C++ type | JSON type |
| ------------------------------------------------------------- |:------------:|
| custom structures/classes | object |
| `bool` | true/false |
| signed and unsigned integral types | number |
| `float` and `double` | number |
| `enum` and `enum class` | number |
| `std::string` | string |
| `const char *` | string |
| iteratable lists (`std::vector`, `std::list`, ...) | array |
| sets (`std::set`, `std::unordered_set`, `std::multiset`, ...) | array |
| `std::tuple` | array |
| `std::unique_ptr`, `std::shared_ptr` | depends/null |
| `std::map`, `std::unordered_map` | object |
| `JsonSerializable` | object |
### Remarks
* Raw pointer are not supported. This prevents
@ -313,7 +314,7 @@ An example for such custom (de)serialization can be found in the file
The following diagram gives an overview about the architecture of the code generator and wrapper library
around RapidJSON:
![Architectue overview](/doc/arch.svg)
![Architectue overview](./doc/arch.svg)
* blue: classes from LibTooling/Clang
* grey: conceivable extension or use

View File

@ -25,6 +25,7 @@ enum class JsonDeserializationErrorKind : byte {
TypeMismatch, /**< The expected type does not match the type actually present in the JSON document. */
ArraySizeMismatch, /**< The expected array size does not match the actual size of the JSON array. A fixed size is expected when deserializing an std::tuple. */
ConversionError, /**< The expected type matches the type present in the JSON document, but further conversion of the value failed. */
UnexpectedDuplicate, /**< The expected type matches the type present in the JSON document, but the value can not be added to the container because it is already present and duplicates are not allowed. */
};
/*!
@ -154,6 +155,7 @@ struct JsonDeserializationErrors : public std::vector<JsonDeserializationError>
template <typename ExpectedType> void reportTypeMismatch(RAPIDJSON_NAMESPACE::Type presentType);
void reportArraySizeMismatch();
void reportConversionError(JsonType jsonType);
void reportUnexpectedDuplicate(JsonType jsonType);
/// \brief The name of the class or struct which is currently being processed.
const char *currentRecord;
@ -162,7 +164,7 @@ struct JsonDeserializationErrors : public std::vector<JsonDeserializationError>
/// \brief The index in the array which is currently processed.
std::size_t currentIndex;
/// \brief The list of fatal error types in form of flags.
enum class ThrowOn : byte { None = 0, TypeMismatch = 0x1, ArraySizeMismatch = 0x2, ConversionError = 0x4 } throwOn;
enum class ThrowOn : byte { None = 0, TypeMismatch = 0x1, ArraySizeMismatch = 0x2, ConversionError = 0x4, UnexpectedDuplicate = 0x8 } throwOn;
private:
void throwMaybe(ThrowOn on) const;
@ -231,6 +233,17 @@ inline void JsonDeserializationErrors::reportConversionError(JsonType jsonType)
throwMaybe(ThrowOn::ConversionError);
}
/*!
* \brief Reports an unexpected duplicate. An error of that kind occurs when the JSON type matched the expected type, but the value can not be inserted in the container because it is already present and duplicates are not allowed.
* \todo Allow specifying the error message.
* \remarks This can happen when doing custom mapping (eg. when interpreting a JSON string as time value).
*/
inline void JsonDeserializationErrors::reportUnexpectedDuplicate(JsonType jsonType)
{
emplace_back(JsonDeserializationErrorKind::UnexpectedDuplicate, jsonType, jsonType, currentRecord, currentMember, currentIndex);
throwMaybe(ThrowOn::UnexpectedDuplicate);
}
} // namespace ReflectiveRapidJSON
#endif // REFLECTIVE_RAPIDJSON_JSON_REFLECTOR_H

View File

@ -18,9 +18,11 @@
#include <limits>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include "./errorhandling.h"
@ -90,9 +92,15 @@ using IsJsonSerializable
// define trait to check for map or hash
template <typename Type>
using IsMapOrHash = Traits::Any<Traits::IsSpecializationOf<Type, std::map>, Traits::IsSpecializationOf<Type, std::unordered_map>>;
template <typename Type> using IsSet = Traits::Any<Traits::IsSpecializationOf<Type, std::set>, Traits::IsSpecializationOf<Type, std::unordered_set>>;
template <typename Type>
using IsArray
using IsMultiSet = Traits::Any<Traits::IsSpecializationOf<Type, std::multiset>, Traits::IsSpecializationOf<Type, std::unordered_multiset>>;
template <typename Type>
using IsArrayOrSet
= Traits::All<Traits::IsIteratable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>, Traits::Not<IsMapOrHash<Type>>>;
template <typename Type>
using IsArray = 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>>>;
// define functions to "push" values to a RapidJSON array or object
@ -194,7 +202,7 @@ inline void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAP
/*!
* \brief Pushes the specified iteratable (eg. std::vector, std::list) to the specified value.
*/
template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::HasSize<Type>>...>
template <typename Type, Traits::EnableIf<IsArrayOrSet<Type>, Traits::HasSize<Type>>...>
void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
{
value.SetArray();
@ -208,7 +216,7 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_
/*!
* \brief Pushes the specified iteratable list (eg. std::vector, std::list) to the specified value.
*/
template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::Not<Traits::HasSize<Type>>>...>
template <typename Type, Traits::EnableIf<IsArrayOrSet<Type>, Traits::Not<Traits::HasSize<Type>>>...>
void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
{
value.SetArray();
@ -346,21 +354,33 @@ void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_N
/*!
* \brief Pulls the specified \a reflectable which is an iteratable without reserve() method from the specified value which is checked to contain an array.
*/
template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::Not<Traits::IsReservable<Type>>>...>
template <typename Type, Traits::EnableIf<IsArrayOrSet<Type>, Traits::Not<Traits::IsReservable<Type>>>...>
void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors);
/*!
* \brief Pulls the specified \a reflectable which is an iteratable with reserve() method from the specified value which is checked to contain an array.
*/
template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::IsReservable<Type>>...>
template <typename Type, Traits::EnableIf<IsArrayOrSet<Type>, Traits::IsReservable<Type>>...>
void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors);
/*!
* \brief Pulls the specified \a reflectable which is an iteratable from the specified array. The \a reflectable is cleared before.
* \brief Pulls the specified \a reflectable which is an array/vector/list from the specified array. The \a reflectable is cleared before.
*/
template <typename Type, Traits::EnableIf<IsArray<Type>>...>
void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ConstArray array, JsonDeserializationErrors *errors);
/*!
* \brief Pulls the specified \a reflectable which is a set from the specified array. The \a reflectable is cleared before.
*/
template <typename Type, Traits::EnableIf<IsSet<Type>>...>
void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ConstArray array, JsonDeserializationErrors *errors);
/*!
* \brief Pulls the specified \a reflectable which is a multiset from the specified array. The \a reflectable is cleared before.
*/
template <typename Type, Traits::EnableIf<IsMultiSet<Type>>...>
void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ConstArray array, JsonDeserializationErrors *errors);
/*!
* \brief Pulls the specified \a reflectable which is a map from the specified value which is checked to contain an object.
*/
@ -492,7 +512,7 @@ inline void pull(Type &, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMES
/*!
* \brief Pulls the specified \a reflectable which is an iteratable without reserve() method from the specified value which is checked to contain an array.
*/
template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::Not<Traits::IsReservable<Type>>>...>
template <typename Type, Traits::EnableIf<IsArrayOrSet<Type>, Traits::Not<Traits::IsReservable<Type>>>...>
void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors)
{
if (!value.IsArray()) {
@ -507,7 +527,7 @@ void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::
/*!
* \brief Pulls the specified \a reflectable which is an iteratable with reserve() method from the specified value which is checked to contain an array.
*/
template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::IsReservable<Type>>...>
template <typename Type, Traits::EnableIf<IsArrayOrSet<Type>, Traits::IsReservable<Type>>...>
void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors)
{
if (!value.IsArray()) {
@ -522,7 +542,7 @@ void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::
}
/*!
* \brief Pulls the specified \a reflectable which is an iteratable from the specified array. The \a reflectable is cleared before.
* \brief Pulls the specified \a reflectable which is an array/vector/list from the specified array. The \a reflectable is cleared before.
*/
template <typename Type, Traits::EnableIf<IsArray<Type>>...>
void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ConstArray array, JsonDeserializationErrors *errors)
@ -537,9 +557,67 @@ void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<c
if (errors) {
errors->currentIndex = index;
}
++index;
reflectable.emplace_back();
pull(reflectable.back(), item, errors);
}
// clear error context
if (errors) {
errors->currentIndex = JsonDeserializationError::noIndex;
}
}
/*!
* \brief Pulls the specified \a reflectable which is a multiset from the specified array. The \a reflectable is cleared before.
*/
template <typename Type, Traits::EnableIf<IsMultiSet<Type>>...>
void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ConstArray array, JsonDeserializationErrors *errors)
{
// clear previous contents of the array
reflectable.clear();
// pull all array elements of the specified value
std::size_t index = 0;
for (const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &item : array) {
// set error context for current index
if (errors) {
errors->currentIndex = index;
}
++index;
typename Type::value_type itemObj;
pull(itemObj, item, errors);
reflectable.emplace(move(itemObj));
}
// clear error context
if (errors) {
errors->currentIndex = JsonDeserializationError::noIndex;
}
}
/*!
* \brief Pulls the specified \a reflectable which is a set from the specified array. The \a reflectable is cleared before.
*/
template <typename Type, Traits::EnableIf<IsSet<Type>>...>
void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>>::ConstArray array, JsonDeserializationErrors *errors)
{
// clear previous contents of the array
reflectable.clear();
// pull all array elements of the specified value
std::size_t index = 0;
for (const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &item : array) {
// set error context for current index
if (errors) {
errors->currentIndex = index;
}
++index;
typename Type::value_type itemObj;
pull(itemObj, item, errors);
if (!reflectable.emplace(move(itemObj)).second) {
errors->reportUnexpectedDuplicate(JsonType::Array);
}
}
// clear error context

View File

@ -33,7 +33,13 @@ using namespace ReflectiveRapidJSON;
// test traits
static_assert(JsonReflector::IsArray<vector<int>>::value, "vector mapped to array");
static_assert(JsonReflector::IsArray<list<int>>::value, "list mapped to array");
static_assert(!JsonReflector::IsArray<string>::value, "string mapped to string");
static_assert(!JsonReflector::IsArray<set<int>>::value, "set not considered an array");
static_assert(!JsonReflector::IsArray<multiset<int>>::value, "multiset not considered an array");
static_assert(JsonReflector::IsArrayOrSet<set<int>>::value, "set is array or set");
static_assert(JsonReflector::IsArrayOrSet<multiset<int>>::value, "multiset is array or set");
static_assert(JsonReflector::IsSet<unordered_set<int>>::value, "set");
static_assert(JsonReflector::IsMultiSet<unordered_multiset<int>>::value, "multiset");
static_assert(!JsonReflector::IsArray<string>::value, "string not mapped to array though it is iteratable");
static_assert(JsonReflector::IsMapOrHash<map<string, int>>::value, "map mapped to object");
static_assert(JsonReflector::IsMapOrHash<unordered_map<string, int>>::value, "hash mapped to object");
static_assert(!JsonReflector::IsMapOrHash<vector<int>>::value, "vector not mapped to object");
@ -49,6 +55,10 @@ struct TestObject : public JsonSerializable<TestObject> {
bool boolean;
map<string, int> someMap;
unordered_map<string, bool> someHash;
set<string> someSet;
multiset<string> someMultiset;
unordered_set<string> someUnorderedSet;
unordered_multiset<string> someUnorderedMultiset;
};
struct NestingObject : public JsonSerializable<NestingObject> {
@ -86,6 +96,10 @@ template <> inline void push<TestObject>(const TestObject &reflectable, Value::O
push(reflectable.boolean, "boolean", value, allocator);
push(reflectable.someMap, "someMap", value, allocator);
push(reflectable.someHash, "someHash", value, allocator);
push(reflectable.someSet, "someSet", value, allocator);
push(reflectable.someMultiset, "someMultiset", value, allocator);
push(reflectable.someUnorderedSet, "someUnorderedSet", value, allocator);
push(reflectable.someUnorderedMultiset, "someUnorderedMultiset", value, allocator);
}
template <> inline void push<NestingObject>(const NestingObject &reflectable, Value::Object &value, Document::AllocatorType &allocator)
@ -115,6 +129,10 @@ inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<ch
pull(reflectable.boolean, "boolean", value, errors);
pull(reflectable.someMap, "someMap", value, errors);
pull(reflectable.someHash, "someHash", value, errors);
pull(reflectable.someSet, "someSet", value, errors);
pull(reflectable.someMultiset, "someMultiset", value, errors);
pull(reflectable.someUnorderedSet, "someUnorderedSet", value, errors);
pull(reflectable.someUnorderedMultiset, "someUnorderedMultiset", value, errors);
if (errors) {
errors->currentRecord = previousRecord;
}
@ -258,8 +276,12 @@ void JsonReflectorTests::testSerializeSimpleObjects()
testObj.boolean = false;
testObj.someMap = { { "a", 1 }, { "b", 2 } };
testObj.someHash = { { "c", true }, { "d", false } };
testObj.someSet = { "a", "b", "c" };
testObj.someMultiset = { "a", "b", "b" };
testObj.someUnorderedSet = { "a" };
testObj.someUnorderedMultiset = { "b", "b", "b" };
CPPUNIT_ASSERT_EQUAL(
"{\"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,
"{\"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,
string(testObj.toJson().GetString()));
}
@ -277,7 +299,7 @@ void JsonReflectorTests::testSerializeNestedObjects()
testObj.text = "test";
testObj.boolean = false;
CPPUNIT_ASSERT_EQUAL(
"{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}}"s,
"{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}}"s,
string(nestingObj.toJson().GetString()));
NestingArray nestingArray;
@ -286,13 +308,13 @@ void JsonReflectorTests::testSerializeNestedObjects()
nestingArray.testObjects.emplace_back(testObj);
nestingArray.testObjects.back().number = 43;
CPPUNIT_ASSERT_EQUAL(
"{\"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,
"{\"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,
string(nestingArray.toJson().GetString()));
vector<TestObject> nestedInVector;
nestedInVector.emplace_back(testObj);
CPPUNIT_ASSERT_EQUAL(
"[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]"s,
"[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}]"s,
string(JsonReflector::toJson(nestedInVector).GetString()));
}
@ -320,7 +342,7 @@ void JsonReflectorTests::testSerializeUniquePtr()
Writer<StringBuffer> jsonWriter(strbuf);
doc.Accept(jsonWriter);
CPPUNIT_ASSERT_EQUAL(
"[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]"s,
"[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}]"s,
string(strbuf.GetString()));
}
@ -348,7 +370,7 @@ void JsonReflectorTests::testSerializeSharedPtr()
Writer<StringBuffer> jsonWriter(strbuf);
doc.Accept(jsonWriter);
CPPUNIT_ASSERT_EQUAL(
"[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{}}]"s,
"[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[]}]"s,
string(strbuf.GetString()));
}
@ -413,8 +435,10 @@ void JsonReflectorTests::testDeserializePrimitives()
*/
void JsonReflectorTests::testDeserializeSimpleObjects()
{
const TestObject testObj(TestObject::fromJson("{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":"
"false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"c\":true,\"d\":false}}"));
const TestObject testObj(
TestObject::fromJson("{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":"
"false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"c\":true,\"d\":false},\"someSet\":[\"a\",\"b\"],\"someMultiset\":["
"\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"b\"],\"someUnorderedMultiset\":[\"a\",\"a\"]}"));
CPPUNIT_ASSERT_EQUAL(42, testObj.number);
CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
@ -425,6 +449,10 @@ void JsonReflectorTests::testDeserializeSimpleObjects()
CPPUNIT_ASSERT_EQUAL(expectedMap, testObj.someMap);
const unordered_map<string, bool> expectedHash{ { "c", true }, { "d", false } };
CPPUNIT_ASSERT_EQUAL(expectedHash, testObj.someHash);
CPPUNIT_ASSERT_EQUAL(set<string>({ "a", "b" }), testObj.someSet);
CPPUNIT_ASSERT_EQUAL(multiset<string>({ "a", "a" }), testObj.someMultiset);
CPPUNIT_ASSERT_EQUAL(unordered_set<string>({ "a", "b" }), testObj.someUnorderedSet);
CPPUNIT_ASSERT_EQUAL(unordered_multiset<string>({ "a", "a" }), testObj.someUnorderedMultiset);
}
/*!
@ -532,7 +560,7 @@ void JsonReflectorTests::testHandlingParseError()
}
/*!
* \brief Tests whether JsonDeserializationError is thrown on type mismatch.
* \brief Tests whether errors are added on type mismatch and in other cases.
*/
void JsonReflectorTests::testHandlingTypeMismatch()
{
@ -544,14 +572,25 @@ void JsonReflectorTests::testHandlingTypeMismatch()
CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":\"42\",\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":"
"\"test\",\"boolean\":false}}",
"\"test\",\"boolean\":false,\"someSet\":[\"a\",\"a\"],\"someMultiset\":[\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"a\"],"
"\"someUnorderedMultiset\":[\"a\",\"a\"]}}",
&errors);
CPPUNIT_ASSERT_EQUAL(1_st, errors.size());
CPPUNIT_ASSERT_EQUAL(3_st, errors.size());
CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::TypeMismatch, errors.front().kind);
CPPUNIT_ASSERT_EQUAL(JsonType::Number, errors.front().expectedType);
CPPUNIT_ASSERT_EQUAL(JsonType::String, errors.front().actualType);
CPPUNIT_ASSERT_EQUAL("number"s, string(errors.front().member));
CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors.front().record));
CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::UnexpectedDuplicate, errors[1].kind);
CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[1].expectedType);
CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[1].actualType);
CPPUNIT_ASSERT_EQUAL("someSet"s, string(errors[1].member));
CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[1].record));
CPPUNIT_ASSERT_EQUAL(JsonDeserializationErrorKind::UnexpectedDuplicate, errors[2].kind);
CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[2].expectedType);
CPPUNIT_ASSERT_EQUAL(JsonType::Array, errors[2].actualType);
CPPUNIT_ASSERT_EQUAL("someUnorderedSet"s, string(errors[2].member));
CPPUNIT_ASSERT_EQUAL("TestObject"s, string(errors[2].record));
errors.clear();
NestingObject::fromJson("{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":1,\"text\":"