mirror of
https://github.com/boostorg/json.git
synced 2026-02-09 23:22:27 +00:00
764 lines
15 KiB
C++
764 lines
15 KiB
C++
//
|
|
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
// Official repository: https://github.com/vinniefalco/json
|
|
//
|
|
|
|
#include <boost/json.hpp>
|
|
|
|
#include <complex>
|
|
#include <iostream>
|
|
|
|
#include "test_suite.hpp"
|
|
|
|
namespace boost {
|
|
namespace json {
|
|
|
|
namespace {
|
|
|
|
void
|
|
usingStrings()
|
|
{
|
|
{
|
|
//[snippet_strings_1
|
|
|
|
string str1; // empty string, default storage
|
|
|
|
string str2( make_counted_resource<monotonic_resource>() ); // empty string, pool storage
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_strings_2
|
|
|
|
std::string sstr1 = "helloworld";
|
|
std::string sstr2 = "world";
|
|
|
|
json::string jstr1 = "helloworld";
|
|
json::string jstr2 = "world";
|
|
|
|
|
|
assert(jstr2.insert(0, jstr1.subview(0, 6)) == "helloworld");
|
|
|
|
// this is equivalent to
|
|
|
|
assert(sstr1.insert(0, sstr2, 0, 6) == "helloworld");
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_strings_3
|
|
|
|
std::string sstr = "hello";
|
|
|
|
json::string jstr = "hello";
|
|
|
|
assert(sstr.append({'w', 'o', 'r', 'l', 'd'}) == "helloworld");
|
|
|
|
// such syntax is inefficient, and the same can
|
|
// be achieved with a character array.
|
|
|
|
assert(jstr.append("world") == "helloworld");
|
|
|
|
//]
|
|
}
|
|
|
|
{
|
|
//[snippet_strings_4
|
|
|
|
json::string str = "Boost.JSON";
|
|
json::string_view sv = str;
|
|
|
|
// all of these call compare(string_view)
|
|
str.compare(sv);
|
|
|
|
str.compare(sv.substr(0, 5));
|
|
|
|
str.compare(str);
|
|
|
|
str.compare("Boost");
|
|
|
|
//]
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
|
|
void
|
|
usingInitLists()
|
|
{
|
|
{
|
|
//[snippet_init_list_1
|
|
|
|
value jv = {
|
|
{ "name", "John Doe" },
|
|
{ "active", true },
|
|
{ "associated-accounts", nullptr },
|
|
{ "total-balance", 330.00 },
|
|
{ "account-balances", { 84, 120, 126 } } };
|
|
|
|
//]
|
|
(void)jv;
|
|
}
|
|
|
|
{
|
|
//[snippet_init_list_2
|
|
|
|
value jv = { true, 2, "hello", nullptr };
|
|
|
|
assert( jv.is_array() );
|
|
|
|
assert( jv.as_array().size() == 4 );
|
|
|
|
assert( to_string(jv) == "[true,2,\"hello\",null]" );
|
|
|
|
//]
|
|
(void)jv;
|
|
}
|
|
|
|
{
|
|
//[snippet_init_list_3
|
|
|
|
value jv = { true, 2, "hello", { "bye", nullptr, false } };
|
|
|
|
assert( jv.is_array() );
|
|
|
|
assert( jv.as_array().back().is_array() );
|
|
|
|
assert( to_string(jv) == "[true,2,\"hello\",[\"bye\",null,false]]" );
|
|
|
|
//]
|
|
(void)jv;
|
|
}
|
|
|
|
{
|
|
//[snippet_init_list_4
|
|
|
|
// Should this be an array or an object?
|
|
value jv = { { "hello", 42 }, { "world", 43 } };
|
|
|
|
//]
|
|
(void)jv;
|
|
}
|
|
|
|
{
|
|
//[snippet_init_list_5
|
|
|
|
value jv1 = { { "hello", 42 }, { "world", 43 } };
|
|
|
|
assert( jv1.is_object() );
|
|
|
|
assert( jv1.as_object().size() == 2 );
|
|
|
|
assert( to_string(jv1) == R"({"hello":42,"world":43})" );
|
|
|
|
// All of the following are arrays
|
|
|
|
value jv2 = { { "make", "Tesla" }, { "model", 3 }, "black" };
|
|
|
|
value jv3 = { { "library", "JSON" }, { "Boost", "C++", "Fast", "JSON" } };
|
|
|
|
value jv4 = { { "color", "blue" }, { 1, "red" } };
|
|
|
|
assert( jv2.is_array() && jv3.is_array() && jv4.is_array() );
|
|
|
|
//]
|
|
(void)jv1;
|
|
(void)jv2;
|
|
(void)jv3;
|
|
(void)jv4;
|
|
}
|
|
|
|
{
|
|
//[snippet_init_list_6
|
|
|
|
value jv = { { "hello", 42 }, array{ "world", 43 } };
|
|
|
|
assert( jv.is_array() );
|
|
|
|
array& ja = jv.as_array();
|
|
|
|
assert( ja[0].is_array() && ja[1].is_array());
|
|
|
|
assert ( to_string(jv) == R"([["hello",42],["world",43]])" );
|
|
|
|
//]
|
|
(void)jv;
|
|
}
|
|
|
|
{
|
|
//[snippet_init_list_7
|
|
|
|
value jv1 = { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } };
|
|
|
|
assert( jv1.is_object() );
|
|
|
|
assert( to_string(jv1) == "{\"mercury\":36,\"venus\":67,\"earth\":93}" );
|
|
|
|
array ja = { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } };
|
|
|
|
for (value& jv2 : ja)
|
|
assert( jv2.is_array() );
|
|
|
|
assert( to_string(ja) == "[[\"mercury\",36],[\"venus\",67],[\"earth\",93]]" );
|
|
|
|
//]
|
|
(void)jv1;
|
|
(void)ja;
|
|
}
|
|
|
|
{
|
|
//[snippet_init_list_8
|
|
|
|
object jo = { { "mercury", { { "distance", 36 } } }, { "venus", { 67, "million miles" } }, { "earth", 93 } };
|
|
|
|
assert( jo["mercury"].is_object() );
|
|
|
|
assert( jo["venus"].is_array() );
|
|
|
|
//]
|
|
(void)jo;
|
|
}
|
|
|
|
{
|
|
//[snippet_init_list_9
|
|
|
|
object jo1 = { { "john", 100 }, { "dave", 500 }, { "joe", 300 } };
|
|
|
|
value jv = { { "clients", std::move(jo1) } };
|
|
|
|
object& jo2 = jv.as_object()["clients"].as_object();
|
|
|
|
assert( ! jo2.empty() && jo1.empty() );
|
|
|
|
assert( to_string(jv) == R"({"clients":{"john":100,"dave":500,"joe":300}})" );
|
|
|
|
//]
|
|
(void)jo1;
|
|
(void)jv;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
|
|
void
|
|
usingArrays()
|
|
{
|
|
{
|
|
//[snippet_arrays_1
|
|
|
|
array arr1; // empty array, default storage
|
|
|
|
array arr2( make_counted_resource<monotonic_resource>() ); // empty array, pool storage
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_arrays_2
|
|
|
|
array arr( { "Hello", 42, true } );
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_arrays_3
|
|
|
|
array arr;
|
|
|
|
arr.emplace_back( "Hello" );
|
|
arr.emplace_back( 42 );
|
|
arr.emplace_back( true );
|
|
|
|
//]
|
|
|
|
//[snippet_arrays_4
|
|
|
|
assert( arr[0].as_string() == "Hello" );
|
|
|
|
// The following line throws std::out_of_range, since the index is out of range
|
|
arr.at( 3 ) = nullptr;
|
|
|
|
//]
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
|
|
void
|
|
usingObjects()
|
|
{
|
|
{
|
|
//[snippet_objects_1
|
|
|
|
object obj1; // empty object, default storage
|
|
|
|
object obj2( make_counted_resource<monotonic_resource>() ); // empty object, pool storage
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_objects_2
|
|
|
|
object obj( {{"key1", "value1" }, { "key2", 42 }, { "key3", false }} );
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_objects_3
|
|
|
|
object obj;
|
|
|
|
obj.emplace( "key1", "value1" );
|
|
obj.emplace( "key2", 42 );
|
|
obj.emplace( "key3", false );
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_objects_4
|
|
|
|
object obj;
|
|
|
|
obj["key1"] = "value1";
|
|
obj["key2"] = 42;
|
|
obj["key3"] = false;
|
|
|
|
// The following line throws std::out_of_range, since the key does not exist
|
|
obj.at( "key4" );
|
|
|
|
//]
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
|
|
void
|
|
usingStorage()
|
|
{
|
|
{
|
|
//[snippet_storage_1
|
|
|
|
value jv; // uses the default storage
|
|
storage_ptr sp; // uses the default storage
|
|
object obj( sp ); // uses the default storage
|
|
|
|
assert( jv.storage().get() == sp.get() ); // same pointer
|
|
assert( *jv.storage() == *sp ); // deep equality
|
|
|
|
assert( jv.storage().get() ==
|
|
obj.storage().get() ); // same pointer
|
|
|
|
//]
|
|
}
|
|
}
|
|
|
|
//[snippet_storage_2
|
|
|
|
value parse_fast( string_view s )
|
|
{
|
|
return parse( s, make_counted_resource<monotonic_resource>() );
|
|
}
|
|
|
|
//]
|
|
|
|
void do_json(value const&) {}
|
|
|
|
//[snippet_storage_3
|
|
|
|
void do_rpc( string_view cmd )
|
|
{
|
|
monotonic_resource mr;
|
|
|
|
value const jv = parse( cmd, &mr );
|
|
|
|
do_json( jv );
|
|
}
|
|
|
|
//]
|
|
|
|
//[snippet_storage_4
|
|
|
|
// TODO
|
|
|
|
//]
|
|
|
|
//----------------------------------------------------------
|
|
|
|
void
|
|
usingParsing()
|
|
{
|
|
{
|
|
//[snippet_parsing_1
|
|
|
|
value jv = parse( "[1,2,3,4,5]" );
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_parsing_2
|
|
|
|
error_code ec;
|
|
value jv = parse( "[1,2,3,4,5]", ec );
|
|
if( ec )
|
|
std::cout << "Parsing failed: " << ec.message() << "\n";
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_parsing_3
|
|
|
|
value jv = parse( "[1,2,3,4,5]", make_counted_resource<monotonic_resource>() );
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_parsing_4
|
|
|
|
parser p;
|
|
|
|
// This must be called once before parsing every new JSON.
|
|
p.start();
|
|
|
|
// Write the entire character buffer, indicating
|
|
// to the parser that there is no more data.
|
|
p.write( "[1,2,3,4,5]", 11 );
|
|
p.finish();
|
|
|
|
// Take ownership of the resulting value.
|
|
value jv = p.release();
|
|
|
|
// At this point the parser may be re-used by calling p.start() again.
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_parsing_5
|
|
|
|
parser p;
|
|
error_code ec;
|
|
|
|
// This must be called once before parsing every new JSON
|
|
p.start();
|
|
|
|
// Write the first part of the buffer
|
|
p.write( "[1,2,", 5, ec);
|
|
|
|
// Write the remaining part of the character buffer,
|
|
// indicating to the parser that there is no more data.
|
|
if(! ec )
|
|
p.write( "3,4,5]", 6, ec );
|
|
if(! ec)
|
|
p.finish( ec );
|
|
|
|
// Take ownership of the resulting value.
|
|
if(! ec)
|
|
value jv = p.release();
|
|
|
|
// At this point the parser may be re-used by calling p.start() again.
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_parsing_6
|
|
|
|
{
|
|
parser p;
|
|
error_code ec;
|
|
|
|
// Declare a new, scoped instance of the block storage
|
|
monotonic_resource mr;
|
|
|
|
// Use the scoped instance for the parsed value
|
|
p.start( &mr );
|
|
|
|
// Write the entire JSON
|
|
p.write( "[1,2,3,4,5]", 11, ec );
|
|
if( ! ec )
|
|
p.finish( ec );
|
|
|
|
// The value will use the instance of block storage created above
|
|
value jv = p.release();
|
|
}
|
|
|
|
//]
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
|
|
void
|
|
usingSerializing()
|
|
{
|
|
{
|
|
//[snippet_serializing_1
|
|
|
|
value jv = { 1, 2, 3 ,4 ,5 };
|
|
|
|
std::cout << jv << "\n";
|
|
|
|
//]
|
|
}
|
|
{
|
|
//[snippet_serializing_2
|
|
|
|
value jv = { 1, 2, 3 ,4 ,5 };
|
|
|
|
string s = to_string( jv );
|
|
|
|
//]
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
|
|
//[snippet_exchange_1
|
|
|
|
struct customer
|
|
{
|
|
std::uint64_t id;
|
|
std::string name;
|
|
bool delinquent;
|
|
|
|
customer() = default;
|
|
|
|
explicit customer( value const& );
|
|
|
|
void to_json( value& jv ) const;
|
|
};
|
|
|
|
//]
|
|
|
|
BOOST_STATIC_ASSERT(
|
|
has_to_value<customer>::value);
|
|
|
|
//BOOST_STATIC_ASSERT(
|
|
// has_from_json<customer>::value);
|
|
|
|
//[snippet_exchange_2
|
|
|
|
void customer::to_json( value& jv ) const
|
|
{
|
|
// Assign a JSON value
|
|
jv = {
|
|
{ "id", id },
|
|
{ "name", name },
|
|
{ "delinquent", delinquent }
|
|
};
|
|
}
|
|
|
|
//]
|
|
|
|
//[snippet_exchange_3
|
|
|
|
customer::customer( value const& 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.
|
|
|
|
: id( jv.at( "id" ).as_uint64() )
|
|
|
|
// We already know that jv is an object from
|
|
// the previous call to jv.as_object() suceeding,
|
|
// now we use jv.get_object() which skips the
|
|
// check.
|
|
//
|
|
// value_cast will throw if jv.kind() != kind::string
|
|
|
|
, name( value_cast< 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
|
|
|
|
this->delinquent = jv.get_object().at( "delinquent" ).as_bool();
|
|
}
|
|
|
|
//]
|
|
|
|
void
|
|
usingExchange1()
|
|
{
|
|
//[snippet_exchange_4
|
|
|
|
customer cust;
|
|
cust.id = 1;
|
|
cust.name = "John Doe";
|
|
cust.delinquent = false;
|
|
|
|
// Convert customer to value
|
|
value jv = to_value( cust );
|
|
|
|
// Store value in customer
|
|
customer cust2;
|
|
cust2 = value_cast< customer >( jv );
|
|
|
|
//]
|
|
}
|
|
|
|
} // (anon)
|
|
|
|
} // json
|
|
} // boost
|
|
//[snippet_exchange_5
|
|
|
|
// Specializations of to_value_traits and value_cast_traits
|
|
// must be declared in the boost::json namespace.
|
|
|
|
namespace boost {
|
|
namespace json {
|
|
|
|
template<class T>
|
|
struct to_value_traits< std::complex< T > >
|
|
{
|
|
static void assign( value& jv, std::complex< T > const& t );
|
|
};
|
|
|
|
template<class T>
|
|
struct value_cast_traits< std::complex< T > >
|
|
{
|
|
static std::complex< T > construct( value const& jv );
|
|
};
|
|
|
|
} // namespace json
|
|
} // namespace boost
|
|
|
|
//]
|
|
namespace boost {
|
|
namespace json {
|
|
|
|
BOOST_STATIC_ASSERT(
|
|
has_to_value<std::complex<float>>::value);
|
|
BOOST_STATIC_ASSERT(
|
|
has_to_value<std::complex<double>>::value);
|
|
BOOST_STATIC_ASSERT(
|
|
has_to_value<std::complex<long double>>::value);
|
|
|
|
BOOST_STATIC_ASSERT(
|
|
has_value_cast<std::complex<float>>::value);
|
|
BOOST_STATIC_ASSERT(
|
|
has_value_cast<std::complex<double>>::value);
|
|
BOOST_STATIC_ASSERT(
|
|
has_value_cast<std::complex<long double>>::value);
|
|
|
|
//[snippet_exchange_6
|
|
|
|
template< class T >
|
|
void
|
|
to_value_traits< std::complex< T > >::
|
|
assign( boost::json::value& jv, std::complex< T > const& t )
|
|
{
|
|
// Store a complex number as a 2-element array
|
|
array& a = jv.emplace_array();
|
|
|
|
// Real part first
|
|
a.emplace_back( t.real() );
|
|
|
|
// Imaginary part last
|
|
a.emplace_back( t.imag() );
|
|
}
|
|
|
|
//]
|
|
|
|
//[snippet_exchange_7]
|
|
|
|
template< class T >
|
|
std::complex< T >
|
|
value_cast_traits< std::complex< T > >::
|
|
construct( value const& jv )
|
|
{
|
|
// as_array() throws if jv.kind() != kind::array.
|
|
|
|
array const& a = jv.as_array();
|
|
|
|
// We store the complex number as a two element
|
|
// array with the real part first, and the imaginary
|
|
// part last.
|
|
//
|
|
// value_cast() throws if the JSON value does
|
|
// not contain an applicable kind for the type T.
|
|
|
|
if(a.size() != 2)
|
|
throw std::invalid_argument(
|
|
"invalid json for std::complex");
|
|
return {
|
|
value_cast< T >( a[0] ),
|
|
value_cast< T >( a[1] ) };
|
|
}
|
|
|
|
//]
|
|
|
|
namespace {
|
|
|
|
void
|
|
usingExchange2()
|
|
{
|
|
{
|
|
//[snippet_exchange_8
|
|
|
|
std::complex< double > c = { 3.14159, 2.71828 };
|
|
|
|
// Convert std::complex< double > to value
|
|
value jv = to_value(c);
|
|
|
|
// Store value in std::complex< double >
|
|
std::complex< double > c2;
|
|
|
|
c2 = value_cast< std::complex< double > >( jv );
|
|
|
|
//]
|
|
}
|
|
|
|
{
|
|
//[snippet_exchange_9
|
|
|
|
// Use float instead of double.
|
|
|
|
std::complex< float > c = { -42.f, 1.41421f };
|
|
|
|
value jv = to_value(c);
|
|
|
|
std::complex< float > c2;
|
|
|
|
c2 = value_cast< std::complex< float > >( jv );
|
|
|
|
//]
|
|
}
|
|
}
|
|
|
|
} // (anon)
|
|
|
|
//----------------------------------------------------------
|
|
|
|
class snippets_test
|
|
{
|
|
public:
|
|
void
|
|
run()
|
|
{
|
|
(void)&usingStrings;
|
|
usingInitLists();
|
|
(void)&usingArrays;
|
|
(void)&usingObjects;
|
|
(void)&usingStorage;
|
|
(void)&parse_fast;
|
|
(void)&do_json;
|
|
(void)&do_rpc;
|
|
(void)&usingParsing;
|
|
(void)&usingSerializing;
|
|
(void)&usingExchange1;
|
|
(void)&usingExchange2;
|
|
BOOST_TEST_PASS();
|
|
}
|
|
};
|
|
|
|
TEST_SUITE(snippets_test, "boost.json.snippets");
|
|
|
|
} // json
|
|
} // boost
|