Value Conversion

While the value container makes it easy to create ad-hoc structures, often it is necessary to convert between JSON and user-defined types or types from the standard library.

The function template value_from provides an interface to construct a value from a type T. The function template value_to converts in the opposite direction, from a type T to value. Both support a wide variety of different fundamental types, such as int or double, standard library types, such as std::string or std::vector<T>, and can be extended to support user-defined types.

std::vector< int > v1{ 1, 2, 3, 4 };

// Convert the vector to a JSON array
value jv = value_from( v1 );
assert( serialize( jv ) == R"([1,2,3,4])" );

// Convert back to vector< int >
std::vector< int > v2 = value_to< std::vector< int > >( jv );
assert( v1 == v2 );

For the type T, the appropriate conversion approach is the first matching category from the following list.

Table 6. Conversion categories
Category of T Comment value_from behavior value_to behavior

Custom conversion

Uses tag_invoke overload.

Custom behavior.

Custom behavior.

Boost.JSON container.

The result is equal to the input value.

The result is equal to the input value.

bool

The result is equal to the input value.

The result is equal to the input value.

Arithmetic type

The result is a number equal to input and has the type

  • std::int64_t, if T is a signed integer; or

  • std::uint64_t, if T is an unsigned integer; or

  • double otherwise.

The result is created via value::to_number.

null_category

Intended for types like std::monostate.

The result is a null value.

The result is default-constructed.

string_category

A sequence of chars, e.g. std::string.

The result is a string.

The result is constructed from a string_view.

variant_category

std::variant and similar types, e.g. boost::variant2::variant.

The result is equal to the result of conversion of the active variant alternative.

The result holds the first alternative for which a conversion succeeds.

optional_category

std::optional and similar types, e.g. boost::optional.

If the input value is empty, the result is a null. Otherwise it is equivalent to conversion of the object stored inside of optional.

The result is default constructed if the input value is null. Otherwise the result is constructed from the result of conversion of the input to the type stored in optional.

map_category

A one-to-one mapping (e.g. std::map) with string-like keys.

The result is an object.

The result is default-constructed, and elements are insert-ed at the end.

sequence_category

A sequence of elements, e.g. std::vector.

The result is an array.

The result is default-constructed, and elements are insert-ed at the end.

tuple_category

A heterogenous sequence with fixed size, e.g. std::tuple and std::pair.

The result is an array.

The result is constructed with the array elements as constructor arguments.

described_class_category

The result is an object with described members' names as keys.

The result is default-constructed and described members are assigned corresponding values.

described_enum_category

If the input value is equal to one of the described enumerators, the result is a string, containing its name. Otherwise it’s equal to the input value converted to its underlying type.

The result is the described enumerator, corresponding to the input string.

path_category

std::filesystem::path and similar types, e.g. boost::filesystem::path.

The result is equal to the result of path::generic_string.

The result is constructed from two pointers to const char.

For composite types (sequences, tuples, described classes, etc.) conversion of contained objects is applied recursively. For example:

std::map< std::string, std::pair<int, bool> > m = {
    {"a", {1, false}},
    {"b", {4, true}},
    {"c", {5, false}},
};

value jv = value_from( m );

assert(( jv == object{
    {"a", array{1, false}},
    {"b", array{4, true}},
    {"c", array{5, false}},
}));

Here, the map is converted to an object, since its conversion category is map_category. Each of its keys is converted into a string, as std::string is of string_category, and each of its values is converted into an array, as std::pair is of tuple_category. Finally, elements of pairs are converted into a std::int64_t number and a bool.