value

JSON documents are represented in memory as instances of value: a Regular type which satisfies DefaultConstructible, CopyConstructible, CopyAssignable, MoveConstructible, MoveAssignable, and many of the requirements of allocator-aware containers. It is implemented as a variant internally, and can dynamically store any of the six defined JSON value types:

  • null: A monostate value, equivalent to nullptr.

  • boolean: A boolean: either true or false.

  • number: An integral or floating point value.

  • string: A sequence of zero or more Unicode characters, similar to std::string.

  • array: An ordered list of values, like std::vector.

  • object: A collection of name/value pairs, also known as an associative array.

Working With Values

A value constructed from nullptr or default constructed represents a null JSON element:

value jv1;
value jv2( nullptr );

assert( jv1.is_null() );
assert( jv2.is_null() );

The member function value::kind may be used to query the kind stored in the value. Alternatively, member functions like value::is_object value::is_number may be used to check whether or not the value is a particular kind:

value jv( object_kind );

assert( jv.kind() == kind::object );
assert( jv.is_object() );
assert( ! jv.is_number() );

Functions like value::if_object actually return a pointer to the object if the value is an object, otherwise they return null. This allows them to be used both in boolean contexts as above, and in assignments or conditional expressions to capture the value of the pointer:

value jv( object_kind );

if( auto p = jv.if_object() )
    return p->size();

After a value is constructed, its kind can change depending on what is assigned to it, or by calling functions such as value::emplace_array or value::emplace_bool. If the assignment is successful, in other words it completes without any exceptions then the value is replaced. Otherwise, the value is unchanged. All operations which can fail to modify a value offer the strong exception safety guarantee.

value jv;
jv = value( array_kind );

assert( jv.is_array() );

jv.emplace_string();

assert( jv.is_string() );

The following table shows all of the ways to determine and access the contents of a !value!:

Table 2. value Accessors

Kind

Representation

Emplacement

Kind Test

Pointer Access

result Access

Checked Access

Unchecked Access

kind::array

array

value::emplace_array

value::is_array

value::if_array

value::try_as_array

value::as_array

value::get_array

kind::object

object

value::emplace_object

value::is_object

value::if_object

value::try_as_object

value::as_object

value::get_object

kind::string

string

value::emplace_string

value::is_string

value::if_string

value::try_as_string

value::as_string

value::get_string

kind::int64

std::int64_t

value::emplace_int64

value::is_int64

value::if_int64

value::try_as_int64

value::as_int64

value::get_int64

kind::uint64

std::uint64_t

value::emplace_uint64

value::is_uint64

value::if_uint64

value::try_as_uint64

value::as_uint64

value::get_uint64

kind::double_

double

value::emplace_double

value::is_double

value::if_double

value::try_as_double

value::as_double

value::get_double

kind::bool_

bool

value::emplace_bool

value::is_bool

value::if_bool

value::try_as_bool

value::as_bool

value::get_bool

kind::null

std::nullptr_t

value::emplace_null

value::is_null

value::try_as_null

The emplace members of value return a typed reference to the underlying representation. For example, the call to value::emplace_string in the previous example returns a string&. This table shows the underlying type for each kind:

Kind Type Description

kind::object

object

An associative array of string keys mapping to value elements with an interface similar to std::unordered_map, that remembers insertion order.

kind::array

array

An ordered list of value elements with an interface similar to std::vector.

kind::string

string

A UTF-8 encoded Unicode string of characters with an interface similar to std::string.

kind::int64

std::int64_t

A 64 bit signed integer.

kind::uint64

std::uint64_t

A 64 bit unsigned integer.

kind::double_

double

A double holding a floating-point value.

kind::bool_

bool

A bool holding true or false.

kind::null

A monostate value representing null.

The return value from emplace can be used to perform assignment or to capture a reference to the underlying element for later inspection or modification:

value jv;
jv.emplace_string() = "Hello, world!";

int64_t& num = jv.emplace_int64();
num = 1;

assert( jv.is_int64() );

If the kind of a value is known, functions such as value::as_bool or value::as_string may be used to obtain a reference to the underlying representation without changing the existing value:

value jv( true );
jv.as_bool() = true;

jv.as_string() = "Hello, world!"; // throws an exception

However, as shown above these functions throw an exception if the kind in the value does not match the kind denoted by the function signature. This can be used as a concise form of validation: access values as if they were the right type, but handle the resulting exception indicating if the schema of the JSON is not valid.

We can query a value for its underlying representation of a particular kind in a way that does not throw exceptions by requesting a pointer which may be null, instead of a reference. Here we use value::if_string to conditionally perform an assignment without using exceptions:

value jv( string_kind );
if( string* str = jv.if_string() )
    *str = "Hello, world!";

If the value’s kind is known statically, a reference to the underlying representation may be obtained by dereferencing the pointer without checking. This avoids the code overhead of the possible exception when using, for example value::as_string:

value jv( string_kind );

// The compiler's static analysis can see that
// a null pointer is never dereferenced.
*jv.if_string() = "Hello, world!";

Functions returning result allow you to use both approaches:

value jv( string_kind );
if( boost::system::result<string&> str = jv.try_as_string() )
    *str = "Hello, world!";

try
{
    jv.try_as_bool().value() = true;
}
catch(...)
{
}

Formatted Output

When a value is formatted to a std::ostream, the result is serialized JSON as if by calling serialize.