...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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:
nullptr
.
true
or false
.
std::string
.
std::vector
.
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
or 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 1.2. value
Accessors
Kind |
Representation |
Emplacement |
Kind Test |
Pointer Access |
Checked Access |
Unchecked Access |
---|---|---|---|---|---|---|
--- |
--- |
--- |
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 |
---|---|---|
An associative array of string keys mapping to |
||
An ordered list of |
||
A UTF-8
encoded Unicode
string
of characters with an interface similar to |
||
|
A 64 bit signed integer. |
|
|
A 64 bit unsigned integer. |
|
|
A |
|
A |
||
--- |
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!";
Tip | |
---|---|
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 jv( string_kind ); // The compiler's static analysis can see that // a null pointer is never dereferenced. *jv.if_string() = "Hello, world!"; |
When a value
is formatted to a std::ostream
,
the result is serialized JSON as if by calling serialize
.