Arbitrary C++ data structures are supported and can be described using the trait json_data_contract
.
Below is an ordinary JSON object
{
"member0": "this is a test",
"member1": 314159,
"member2": true
}
The above JSON describes a class with three members, a string named member0
, an integer named member1
, and a boolean named member2
Below is the C++ data structure and the trait to map the members to that of the JSON object. Note that the names of the C++ data members do not have to be the same as the JSON object's. To see a working example using this code, refer to cookbook_class1_test.cpp
struct MyClass1 {
std::string member_0;
int member_1;
bool member_2;
};
namespace daw::json {
template<>
struct json_data_contract<MyClass1> {
using type = json_member_list<
json_string<"member0">,
json_number<"member1", int>,
json_bool<"member2">
>;
static inline auto to_json_data( MyClass1 const &value ) {
return std::forward_as_tuple(
value.member_0,
value.member_1,
value.member_2 );
}
};
}
The above json_data_contract
trait maps the JSON members to the constructor of MyClass1
in the order specified. The arguments of type std::string, int, bool
will be passed.
The serializing and deserializing is recursive. So if a class contains another class, the requirement is that that class has been mapped already. Assuming we already have the mapping above, lets embed that into another class
{
"a": {
"member0": "this is a test",
"member1": 314159,
"member2": true
},
"b": 1234
}
The JSON object that has a member "a"
that matches MyClass1
, and a second member is an unsigned
To see a working example using this code, refer to cookbook_class2_test.cpp
// Code from previous MyClass1 example
struct MyClass2 {
MyClass1 a;
unsigned b;
};
namespace daw::json {
template<>
struct json_data_contract<MyClass2> {
using type = json_member_list<
json_class<"a", MyClass1>,
json_number<"b", unsigned>
>;
static inline auto
to_json_data( MyClass2 const &value ) {
return std::forward_as_tuple( value.a, value.b );
}
};
} // namespace daw::json
Not all the JSON objects members need to be mapped. Below is the same JSON object as in the MyClass2
example above.
To see a working example using this code, refer to cookbook_class3_test.cpp
{
"a": {
"member0": "this is a test",
"member1": 314159,
"member2": true
},
"b": 1234
}
// Code from previous MyClass1 example
struct MyClass3 {
MyClass1 a;
};
namespace daw::json {
template<>
struct json_data_contract<MyClass3> {
using type = json_member_list<json_class<"a", MyClass1>>;
static inline auto
to_json_data( MyClass3 const &value ) {
return std::forward_as_tuple( value.a );
}
};
}
Only the "a"
member is mapped, the "b"
member of the JSON object is ignored.
If a type is already mapped or has many possible JSON mappings you can provide an alternate mapping. This is accomplished by mapping to the daw::json::json_alt<Type, Index>
type. The optional index allows for any number of mappings that fit into a std::size_t
.
To see a working example using this code, refer to cookbook_class_alternate1_test.cpp
struct Thing {
int a;
};
namespace daw::json {
template<>
struct json_data_contract<Thing> {
using type = json_member_list<json_number<"a", int>>;
};
struct json_data_contract<json_alt<Thing>> {
using type = json_ordered_member_list<int>;
};
}
//...
Thing a = daw::json::from_json<Thing>( json_string_a );
Thing b = daw::json::from_json<daw::json::json_alt<Thing>>( json_string_b );
If constructing the class requires extra steps than passing the parsed members to it's constructor one can provide a custom Constructor callable as the default for that type or for a specific json_class
mapping.
If one wants to change the default Constructor type of a specific type they can add a type alias in that classes json_data_contract
. For instance, if a member needs extra checking that the types constructor does not provide:
struct FooConstructor {
constexpr Foo operator( )( std::string name, .... ) const {
if( name != "bobfred" ) { throw badname{}; }
return Foo{ name, .... };
}
};
template<>
struct json_data_constract<Foo> {
using constructor_t = FooConstructor;
using type = json_member_list<json_string<"name">,...>;
};
Here the type Foo
will use a callable of type FooConstructor
to create all Foo
's in the library unless overridden by a mapping.
The json_class
mapping's second parameter is that of a Constructor type to call to construct that class and override the default. It can be specified like
json_class<"name", FooConstructor>