DAW JSON Link
Variant Types

Variant or sum types are where the json member can have more than one type(like string, number, class, or array).

Take the following JSON

[
{
"member0": 5,
"member1": "hello"
},
{
"member0": "world",
"member1": true
}
]

Here we have an array of a class that has two members. A variant of types number and string, member0; and a variant of type string and bool, member1.

Too see a working example using this code, refer to cookbook_variant1_test.cpp

The following C++ can provide a mapping. It, also, highlights that the types bool, integers, floating point, std:: string, and previously mapped types can be default mapped to elements that do not require a name. Such as variant, array, and some key_value's.

{c++}

Important note:

The elements in the json_variant_type_list must have matching types in the variant alternatives. (e.g. std::string -> json_string, bool -> json_bool )

Tagged Variants

It is common to have a tag discriminator in JSON data. The json_tagged_variant member type allows using another parsed member to return an index in a member list to parse. This allows any number of types to be inside the variant.

Below is a JSON array, containing a variant element where the "type" member determines the type of the "value" member. In many JSON documents, the discriminator will be a string.

[
{
"type": 0,
"name": "string value",
"value": "hello"
},
{
"type": 1,
"name": "int value",
"value": 5
},
{
"type": 2,
"name": "bool value",
"value": false
}
]

A member name and a callable are needed to tell the parser which type will parsed.

Too see a working example using this code, refer to cookbook_variant2_test.cpp

{c++}

In the above example, two members are mapped to construct MyClass, "name" and "value". The variant uses the JSON "type" member to determine the index of the parser to use for the variant value.

Extending the previous example, it auto detected the std::string, int, and bool types and supplied the parser descriptions for them. Lets do it manually.

Below is a JSON array, containing a variant element where the "type" member determines the type of the "value" member. In many JSON documents, the discriminator will be a string.

[
{
"type": 0,
"name": "string value",
"value": "hello"
},
{
"type": 1,
"name": "int value",
"value": 5
},
{
"type": 2,
"name": "bool value",
"value": false
}
]

A member name and a Callable are needed to tell the parser which type will parsed.

Too see a working example using this code, refer to cookbook_variant3_test.cpp

{c++}

As you can see, the json_tagged_variant_type_list can use terse type names for some, or the full names.

Submember as tag for tagged_variant

There are cases where a classes structure is determined by one of it's submembers. This comes up with file versioning.

In our example we have two versions of a config file. The tag member "version" determines the layout of the other members in the example.

{
"version": 1,
"name": "what is the answer to the ultimate question?",
"value": 42,
"next question": "what is earth"
}
{
"version": 2,
"config_options": [
{
"name": "bob",
"value": 42
}
],
"option2": 5
}

The above example shows two distinct JSON objects that both have a "version" member that is a discriminator for the expected data structure.

namespace version1 {
struct Config {
int version;
std::string name;
int value;
std::string next_question;
};
}
namespace version2 {
struct Config {
int version;
std::map<std::string, int> config_options;
int option2;
};
}
using configs_t = std::variant<version1::Config, version2::Config>;
struct Switcher {
// Convert JSON tag member to type index
size_t operator( )( int type ) const {
return static_cast<std::size_t>( type - 1 );
}
// Get value for Tag from class value
std::size_t operator( )( configs_t const &v ) const {
return static_cast<std::size_t>( v.index( ) );
}
};
namespace daw::json {
template<>
struct json_data_contract<version1::Config> {
using type = json_member_list<
json_number<"version", int>,
json_string<"name">,
json_number<"value", int>,
json_string<"next question">
>;
static constexpr auto to_json_data( version1::Config const & v ) {
return std::forward_as_tuple( v.version, v.name, v.value, v.next_question );
}
};
template<>
struct json_data_contract<version2::Config> {
using type = json_member_list<
json_number<"version", int>,
json_key_value_array<"config_options",
std::map<std::string, int>,
json_number<"value", int>,
json_string<"name">
>,
json_number<"option2", int>
>;
static constexpr auto to_json_data( version2::Config const & v ) {
return std::forward_as_tuple( v.version, v.config_options, v.option2 );
}
};
template<>
struct json_data_contract<std::variant<version1::Config, version2::Config>> {
using type = json_submember_tagged_variant<
json_number<"version", int>,
Switcher,
version1::Config,
version2::Config
>;
};
}
daw::json
Definition: daw_json_event_parser.h:17
daw::json::json_data_contract::type
missing_json_data_contract_for< T > type
Definition: daw_json_traits.h:40