|  | Home | Libraries | People | FAQ | More | 
        While the value
        container makes it easy to create ad-hoc structures, often it is necessary
        to convert between JSON and specific user-defined types. Converting from
        a type T to value is done by value_from. The conversion in the
        opposite direction is done with value_to.
      
std::vector< int > v1{ 1, 2, 3, 4 }; // Convert the vector to a JSON array value jv = value_from( v1 ); assert( jv.is_array() ); array& ja = jv.as_array(); assert( ja.size() == 4 ); for ( std::size_t i = 0; i < v1.size(); ++i ) assert( v1[i] == ja[i].as_int64() ); // Convert back to vector< int > std::vector< int > v2 = value_to< std::vector< int > >( jv ); assert( v1 == v2 );
        A customization point is a mechanism where a library
        delegates behavior of some operation to the user, or gives the user the option
        of controlling the behavior of some operation for a specific type. Within
        the standard library, the swap
        function is a customization point that uses argument-dependent
        lookup to find user-provided overloads within the namespace of the
        arguments:
      
template< class T > void identity_swap( T& a, T& b ) { // introduces the declaration of // std::swap into this scope using std::swap; if( &a == &b ) return; // the overload set will contain std::swap, // any declarations of swap within the enclosing // namespace, and any declarations of swap within // the namespaces associated with T swap( a, b ); }
        Another example would be the class template std::hash,
        which can be specialized for some type T
        to implement custom behavior:
      
template< std::size_t N > struct static_string { }; namespace std { template< std::size_t N > class hash< static_string< N > > { public: std::size_t operator()(const static_string< N >& str ) const noexcept { return std::hash< std::string >()( str ); } }; } // std
        While these mechanisms work, they are not without problems. Boost.JSON implements
        value conversion customization points using the tag_invoke
        mechanism outlined in P1895,
        allowing users to define conversions to and from their own types. In essence,
        tag_invoke provides a uniform
        interface for defining customization points by using argument-dependent lookup
        to find a viable function from the point at which it's called. As the name
        suggests, a tag type is passed as an argument in order to:
      
value_to_tag<T>) so that its associated
            namespaces and entities are examined when name lookup is performed.
          
        This has the effect of finding user-provided tag_invoke
        overloads, even if they are declared (lexically) after the definition of
        the calling function.
      
tag_invoke overloads
      
        In all cases, conversions are done by calling an appropriate overload of
        tag_invoke. For value_from, these have the form:
      
void tag_invoke( const value_from_tag&, value&, T );
        Likewise, the overloads of tag_invoke
        called by value_to
        take the form:
      
T tag_invoke( const value_to_tag< T >&, const value& );
In both cases, overloads for user-provided types can be implemented:
template< class T > void tag_invoke( const value_from_tag&, value& jv, std::complex< T > const& t) { // Store a complex number as a 2-element array // with the real part followed by the imaginary part jv = { t.real(), t.imag() }; } template< class T > std::complex< T > tag_invoke( const value_to_tag< std::complex< T > >&, value const& jv ) { return std::complex< T >( jv.as_array().at(0).to_number< T >(), jv.as_array().at(1).to_number< T >()); }
Since the type being converted is embedded into the function's signature, user-provided overloads are visible to argument-dependent lookup and will be candidates when a conversion is performed:
template< class T > struct vec3 { T x, y, z; }; template< class T > void tag_invoke( const value_from_tag&, value& jv, const vec3<T>& vec ) { jv = { { "x", vec.x }, { "y", vec.y }, { "z", vec.z } }; }
        When value_from
        is called, the tag_invoke
        function template will be found by argument-dependent lookup and used to
        perform the conversion:
      
vec3< int > pos = { 4, 1, 4 }; value jv = value_from( pos ); assert( serialize( jv ) == "{\"x\":4,\"y\":1,\"z\":4}" );
        In addition to user-provided overloads of tag_invoke,
        the library will add its own function to the overload set when certain constraints
        are satisfied. The library provided overloads have no special prioritization
        over those provided by the user, so care should be taken to avoid writing
        ambiguous declarations:
      
template< class T, typename std::enable_if< std::is_floating_point< T >::value>::type* = nullptr > void tag_invoke( const value_from_tag&, value& jv, T t ) { jv = std::llround( t ); }
Upon calling this function, overload resolution will fail because the library already provides an overload for floating-point types:
value jv = value_from( 1.5 ); // error
        Library-provided overloads of tag_invoke
        come in two variants: those that convert between JSON types (known as built-in
        conversions), and those that convert to/from container and string
        types (known as generic conversions). Generic conversions
        offer convenience by eliminating the need to write repetitive overloads for
        types that model common C++ concepts (e.g. sequence containers, associative
        containers, tuples, and strings).
      
std::map< std::string, vec3< int > > positions = { { "Alex", { 42, -60, 18 } }, { "Blake", { 300, -60, -240} }, { "Carol", { -60, 30, 30 } } }; // conversions are applied recursively; // the key type and value type will be converted // using value_from as well value jv = value_from( positions ); assert( jv.is_object() ); object& jo = jv.as_object(); assert( jo.size() == 3 ); // The sum of the coordinates is 0 assert( std::accumulate( jo.begin(), jo.end(), std::int64_t(0), []( std::int64_t total, const key_value_pair& jp ) { assert ( jp.value().is_object() ); const object& pos = jp.value().as_object(); return total + pos.at( "x" ).as_int64() + pos.at( "y" ).as_int64() + pos.at( "z" ).as_int64(); } ) == 0 );
        The function template value_from provides an interface
        to construct a value from a user- or library-provided
        type T. The optionally supplied
        storage_ptr
        argument is used as the memory_resource for the resulting
        value
        object. The parameter of type value& is the result of the conversion; this
        ensures that the storage_ptr is correctly propagated
        to the result. For example, consider the following struct:
      
struct customer { std::uint64_t id; std::string name; bool late; customer() = default; customer( std::uint64_t i, const std::string& n, bool l ) : id( i ), name( n ), late( l ) { } explicit customer( value const& ); }; void tag_invoke( const value_from_tag&, value& jv, customer const& c ) { // Assign a JSON value jv = { { "id", c.id }, { "name", c.name }, { "late", c.late } }; }
        If our store has a lot of customers, it may be desirable to use a monotonic_resource when serializing
        customer objects to JSON.
        value_from
        ensures that the correct memory_resource is used:
      
std::vector< customer > customers = { customer( 0, "Alison", false ), customer( 1, "Bill", false ), customer( 3, "Catherine", true ), customer( 4, "Doug", false ) }; storage_ptr sp = make_shared_resource< monotonic_resource >(); value jv = value_from( customers, sp ); assert( jv.storage() == sp ); assert( jv.is_array() );
        In addition to the user-provided overloads found by argument-dependent lookup,
        the library provides its own overload of tag_invoke
        when certain conditions are met.
      
        If, for the type T being
        converted
      
std::is_assignable<value&,
            T&&>::value is true,
            or
          T satisfies StringLike,
            or
          T satisfies FromMapLike,
            or
          T satisfies FromContainerLike,
            or
          T satisfies TupleLike.
          Then a function template of the form
template< class T > void tag_invoke( value_from_tag, value& jv, T&& t );
        is added to the set of user-provided overloads found by argument-dependent
        lookup; it performs the conversion corresponding to first condition met by
        T in the above list. For
        example, if T satisfies both
        FromMapLike and FromContainerLike,
        the conversion will be performed the one corresponding to FromMapLike;
        it will not be ambiguous.
      
// Satisfies both FromMapLike and FromContainerLike std::unordered_map< std::string, bool > available_tools = { { "Crowbar", true }, { "Hammer", true }, { "Drill", true }, { "Saw", false }, }; value jv = value_from( available_tools ); assert( jv.is_object() );
        The conversion performed when the first condition is met (the library-provided
        built-in conversion) is assignment to the value parameter. For the generic
        conversions, types that satisfy TupleLike or FromContainerLike
        are converted to array, those that satisfy FromMapLike
        are converted to object, and types that satisfy
        StringLike are converted to string.
      
        The function template value_to provides an interface to
        construct a type T from a
        value.
        In contrast to value_from, no output parameter
        is used as there is no storage_ptr to propagate.
      
std::complex< double > c1 = { 3.14159, 2.71828 }; // Convert a complex number to JSON value jv = value_from( c1 ); assert ( jv.is_array() ); // Convert back to a complex number std::complex< double > c2 = value_to< std::complex< double > >( jv );
        As with value_from,
        the library provides its own overload of tag_invoke
        when certain conditions are met.
      
        If, for the type T
      
T is value, object, array, string, string_view, __value_ref__,
            std::initializer_list<value_ref>
            or bool, or if std::is_arithmetic<T>::value is true,
            or
          T satisfies StringLike,
            or
          T satisfies ToMapLike,
            or
          T satisfies ToContainerLike,
            or
          T satisfies TupleLike.
          Then a function template of the form
template< class T > T tag_invoke( value_to_tag< T >, const value& jv );
        is added to the set of user-provided overloads found by argument-dependent
        lookup. As with value_from, it performs the conversion
        corresponding to first condition met by T
        in the above list. Given the following definition of customer::customer( const value& ):
      
customer tag_invoke( const value_to_tag<customer>&, const value& jv ) { // at() throws if `jv` is not an object, or if the key is not found. // as_uint64() will throw if the value is not an unsigned 64-bit integer. std::uint64_t id = jv.at( "id" ).as_uint64(); // We already know that jv is an object from // the previous call to jv.as_object() succeeding, // now we use jv.get_object() which skips the // check. value_to will throw if jv.kind() != kind::string std::string name = value_to< std::string >( jv.get_object().at( "name" ) ); // id and name are constructed from JSON in the member // initializer list above, but we can also use regular // assignments in the body of the function as shown below. // as_bool() will throw if kv.kind() != kind::bool bool late = jv.get_object().at( "late" ).as_bool(); return customer(id, name, late); }
        Objects of type customer
        can be converted to and from value:
      
customer c1( 5, "Ed", false ); // Convert customer to value value jv = value_from( c1 ); // Convert the result back to customer customer c2 = value_to< customer >( jv ); // The resulting customer is unchanged assert( c1.name == c2.name );
        When the first condition is met, the conversion will simply return the object
        of type T stored within the
        value
        (e.g. using jv.as_object(),
        jv.as_array(),
        etc). When the second condition is met, the result of the conversion will
        be T(jv). As
        with value_from,
        when generic conversions are selected, an attempt will be made to convert
        the value
        to T.
      
value available_tools = { { "Crowbar", true }, { "Hammer", true }, { "Drill", true }, { "Saw", false } }; assert( available_tools.is_object() ); auto as_map = value_to< std::map< std::string, bool > >( available_tools ); assert( available_tools.as_object().size() == as_map.size() );
| ![[Important]](../../../../../../doc/src/images/important.png) | Important | 
|---|---|
| The contents of this subsection do not apply to standalone mode | 
        Library authors may wish to provide conversions between types in their libraries
        and value.
        It's possible to do this even without adding a physical dependency on Boost.JSON
        with the help of a few forward declarations.
      
namespace boost { namespace json { class value; struct value_from_tag; template< class T > struct value_to_tag; template< class T > T value_to( const value& v ); template<class T> void value_from( T&& t, value& jv ); } }
        Note that value_from is declared using an
        out-parameter, rather then returning its result. This overload is specifically
        designed for this use-case.
      
        After that the definitions of tag_invoke
        overloads should be provided. These overlaods have to be templates, since
        value
        is only forward-declared and hence is an incomplete type.
      
namespace thirdparty { template< class JsonValue > customer tag_invoke( const boost::json::value_to_tag<customer>&, const JsonValue& jv ) { std::uint64_t id = boost::json::value_to<std::uint64_t>( jv.at( "id" ) ); std::string name = boost::json::value_to< std::string >( jv.at( "name" ) ); bool late = boost::json::value_to<bool>( jv.at( "late" ) ); return customer(id, std::move(name), late); } template< class JsonValue > void tag_invoke( const boost::json::value_from_tag&, JsonValue& jv, const customer& c) { auto& obj = jv.emplace_object(); boost::json::value_from(c.id, obj["id"]); boost::json::value_from(c.name, obj["name"]); boost::json::value_from(c.late, obj["late"]); } }
        Each of the following tables specify valid operations on a type or expression
        thereof meeting the requirement R. A requirement Req
        prefixed with From/To does not define a single requirement;
        it defines the two requirements FromReq and ToReq
        which correspond to value_to and value_from, respectively.
      
In each of the following:
T is a type that satisfies
            R,
          e is an lvalue of type
            T,
          has_value_trait names
            the template has_value_to (when R
            is prefixed with To) or has_value_from (when R
            is prefixed with From).
          using
            std::begin;
            and using std::end; precede the point at which the validity
            and semantics of an expression is determined.
          Table 1.3. Valid expressions
| Expression | Type | Semantics and Constraints | 
|---|---|---|
| 
                   | 
                   | 
                  Constraints:  | 
Table 1.4. Valid expressions
| Expression | Type | Semantics and Constraints | 
|---|---|---|
| 
                   | 
                   | 
                  Constraints:  | 
| 
                   | 
                   | 
                  Constraints:  | 
| 
                   | 
                   | 
                  Constraints:  | 
Table 1.5. Valid expressions
| Expression | Type | Semantics and Constraints | 
|---|---|---|
| 
                   | 
                   | 
                  Constraints:  | 
| 
                   | 
                   | |
| 
                   | 
                   | 
        In the following table vt
        is a prvalue of type T::value_type.
      
Table 1.6. Valid expressions
| Expression | Type | Semantics and Constraints | 
|---|---|---|
| 
                   | 
                   | |
| 
                   | 
                   | |
| 
                   | 
                   | 
                  Constraints:  | 
| 
                   | 
                   | 
                  Constraints for FromMapLike:  
                  Constraints for ToMapLike:  | 
| 
                   | 
                   | 
                  Constraints:  | 
| 
                   | 
                   | 
                  Constraints:  |