2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 // Official repository: https://github.com/boostorg/json
10 #include <boost/json.hpp>
12 #ifndef BOOST_JSON_STANDALONE
21 #include <unordered_map>
24 #include "test_suite.hpp"
27 # pragma warning(push)
28 # pragma warning(disable: 4101)
29 #elif defined(__clang__)
30 # pragma clang diagnostic push
31 # pragma clang diagnostic ignored "-Wunused"
32 # pragma clang diagnostic ignored "-Wmismatched-tags"
33 #elif defined(__GNUC__)
34 # pragma GCC diagnostic push
35 # pragma GCC diagnostic ignored "-Wunused"
40 template< std::size_t N
>
41 struct static_string
{ };
46 template< std::size_t N
>
47 class hash
< static_string
< N
> >
51 operator()(const static_string
< N
>& str
) const noexcept
53 return std::hash
< std::string
>()( str
);
67 tag_invoke( const value_from_tag
&, value
& jv
, std::complex< T
> const& t
)
69 // Store a complex number as a 2-element array
70 // with the real part followed by the imaginary part
71 jv
= { t
.real(), t
.imag() };
76 tag_invoke( const value_to_tag
< std::complex< T
> >&, value
const& jv
)
78 return std::complex< T
>(
79 jv
.as_array().at(0).to_number
< T
>(),
80 jv
.as_array().at(1).to_number
< T
>());
93 string str1
; // empty string, uses the default memory resource
95 string
str2( make_shared_resource
<monotonic_resource
>() ); // empty string, uses a counted monotonic resource
102 std::string sstr1
= "helloworld";
103 std::string sstr2
= "world";
105 json::string jstr1
= "helloworld";
106 json::string jstr2
= "world";
108 assert( jstr2
.insert(0, jstr1
.subview(0, 5)) == "helloworld" );
110 // this is equivalent to
111 assert( sstr2
.insert(0, sstr1
, 0, 5) == "helloworld" );
118 std::string sstr
= "hello";
120 json::string jstr
= "hello";
122 assert(sstr
.append({'w', 'o', 'r', 'l', 'd'}) == "helloworld");
124 // such syntax is inefficient, and the same can
125 // be achieved with a character array.
127 assert(jstr
.append("world") == "helloworld");
135 json::string str
= "Boost.JSON";
136 json::string_view sv
= str
;
138 // all of these call compare(string_view)
141 str
.compare(sv
.substr(0, 5));
145 str
.compare("Boost");
151 //----------------------------------------------------------
160 value
jv2( nullptr );
162 assert( jv1
.is_null() );
163 assert( jv2
.is_null() );
170 value
jv( object_kind
);
172 assert( jv
.kind() == kind::object
);
173 assert( jv
.is_object() );
174 assert( ! jv
.is_number() );
182 value
jv( object_kind
);
184 if( auto p
= jv
.if_object() )
188 return std::size_t(0);
196 jv
= value( array_kind
);
198 assert( jv
.is_array() );
202 assert( jv
.is_string() );
210 jv
.emplace_string() = "Hello, world!";
212 int64_t& num
= jv
.emplace_int64();
215 assert( jv
.is_int64() );
227 jv
.as_string() = "Hello, world!"; // throws an exception
238 value
jv( string_kind
);
239 if( string
* str
= jv
.if_string() )
240 *str
= "Hello, world!";
247 value
jv( string_kind
);
249 // The compiler's static analysis can see that
250 // a null pointer is never dereferenced.
251 *jv
.if_string() = "Hello, world!";
257 //----------------------------------------------------------
263 //[snippet_init_list_1
266 { "name", "John Doe" },
268 { "associated-accounts", nullptr },
269 { "total-balance", 330.00 },
270 { "account-balances", { 84, 120, 126 } } };
276 //[snippet_init_list_2
278 value jv
= { true, 2, "hello", nullptr };
280 assert( jv
.is_array() );
282 assert( jv
.as_array().size() == 4 );
284 assert( serialize(jv
) == "[true,2,\"hello\",null]" );
290 //[snippet_init_list_3
292 value jv
= { true, 2, "hello", { "bye", nullptr, false } };
294 assert( jv
.is_array() );
296 assert( jv
.as_array().back().is_array() );
298 assert( serialize(jv
) == "[true,2,\"hello\",[\"bye\",null,false]]" );
304 //[snippet_init_list_4
306 // Should this be an array or an object?
307 value jv
= { { "hello", 42 }, { "world", 43 } };
313 //[snippet_init_list_5
315 value jv1
= { { "hello", 42 }, { "world", 43 } };
317 assert( jv1
.is_object() );
319 assert( jv1
.as_object().size() == 2 );
321 assert( serialize(jv1
) == R
"({"hello
":42,"world
":43})" );
323 // All of the following are arrays
325 value jv2
= { { "make", "Tesla" }, { "model", 3 }, "black" };
327 value jv3
= { { "library", "JSON" }, { "Boost", "C++", "Fast", "JSON" } };
329 value jv4
= { { "color", "blue" }, { 1, "red" } };
331 assert( jv2
.is_array() && jv3
.is_array() && jv4
.is_array() );
337 //[snippet_init_list_6
339 value jv
= { { "hello", 42 }, array
{ "world", 43 } };
341 assert( jv
.is_array() );
343 array
& ja
= jv
.as_array();
345 assert( ja
[0].is_array() && ja
[1].is_array());
347 assert ( serialize(jv
) == R
"([["hello
",42],["world
",43]])" );
355 //[snippet_init_list_7
357 value jv
= { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } };
359 assert( jv
.is_object() );
361 assert( serialize(jv
) == "{\"mercury\":36,\"venus\":67,\"earth\":93}" );
363 array ja
= { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } };
365 assert( serialize(ja
) == "[[\"mercury\",36],[\"venus\",67],[\"earth\",93]]" );
373 //[snippet_init_list_8
375 object jo
= { { "mercury", { { "distance", 36 } } }, { "venus", { 67, "million miles" } }, { "earth", 93 } };
377 assert( jo
["mercury"].is_object() );
379 assert( jo
["venus"].is_array() );
385 //[snippet_init_list_9
387 object jo1
= { { "john", 100 }, { "dave", 500 }, { "joe", 300 } };
389 value jv
= { { "clients", std::move(jo1
) } };
391 object
& jo2
= jv
.as_object()["clients"].as_object();
393 assert( ! jo2
.empty() && jo1
.empty() );
395 assert( serialize(jv
) == R
"({"clients
":{"john
":100,"dave
":500,"joe
":300}})" );
403 //----------------------------------------------------------
411 array arr1
; // empty array, uses the default memory resource
413 array
arr2( make_shared_resource
<monotonic_resource
>() ); // empty array, uses a counted monotonic resource
420 array
arr( { "Hello", 42, true } );
430 arr
.emplace_back( "Hello" );
431 arr
.emplace_back( 42 );
432 arr
.emplace_back( true );
438 assert( arr
[0].as_string() == "Hello" );
440 // The following line throws std::out_of_range, since the index is out of range
441 arr
.at( 3 ) = nullptr;
450 //----------------------------------------------------------
458 object obj1
; // empty object, uses the default memory resource
460 object
obj2( make_shared_resource
<monotonic_resource
>() ); // empty object, uses a counted monotonic resource
467 object
obj( {{"key1", "value1" }, { "key2", 42 }, { "key3", false }} );
476 obj
.emplace( "key1", "value1" );
477 obj
.emplace( "key2", 42 );
478 obj
.emplace( "key3", false );
488 obj
["key1"] = "value1";
492 // The following line throws std::out_of_range, since the key does not exist
502 //----------------------------------------------------------
507 void identity_swap( T
& a
, T
& b
)
509 // introduces the declaration of
510 // std::swap into this scope
514 // the overload set will contain std::swap,
515 // any declarations of swap within the enclosing
516 // namespace, and any declarations of swap within
517 // the namespaces associated with T
532 void tag_invoke( const value_from_tag
&, value
& jv
, const vec3
<T
>& vec
)
543 #ifdef BOOST_JSON_DOCS
546 template< class T
, typename
std::enable_if
<
547 std::is_floating_point
< T
>::value
>::type
* = nullptr >
548 void tag_invoke( const value_from_tag
&, value
& jv
, T t
)
550 jv
= std::llround( t
);
564 customer() = default;
566 customer( std::uint64_t i
, const std::string
& n
, bool l
)
567 : id( i
), name( n
), late( l
) { }
569 explicit customer( value
const& );
572 void tag_invoke( const value_from_tag
&, value
& jv
, customer
const& c
)
574 // Assign a JSON value
586 customer
tag_invoke( const value_to_tag
<customer
>&, const value
& jv
)
588 // at() throws if `jv` is not an object, or if the key is not found.
589 // as_uint64() will throw if the value is not an unsigned 64-bit integer.
590 std::uint64_t id
= jv
.at( "id" ).as_uint64();
592 // We already know that jv is an object from
593 // the previous call to jv.as_object() succeeding,
594 // now we use jv.get_object() which skips the
595 // check. value_to will throw if jv.kind() != kind::string
596 std::string name
= value_to
< std::string
>( jv
.get_object().at( "name" ) );
598 // id and name are constructed from JSON in the member
599 // initializer list above, but we can also use regular
600 // assignments in the body of the function as shown below.
601 // as_bool() will throw if kv.kind() != kind::bool
602 bool late
= jv
.get_object().at( "late" ).as_bool();
604 return customer(id
, name
, late
);
615 std::vector
< int > v1
{ 1, 2, 3, 4 };
617 // Convert the vector to a JSON array
618 value jv
= value_from( v1
);
620 assert( jv
.is_array() );
622 array
& ja
= jv
.as_array();
624 assert( ja
.size() == 4 );
626 for ( std::size_t i
= 0; i
< v1
.size(); ++i
)
627 assert( v1
[i
] == ja
[i
].as_int64() );
629 // Convert back to vector< int >
630 std::vector
< int > v2
= value_to
< std::vector
< int > >( jv
);
641 vec3
< int > pos
= { 4, 1, 4 };
643 value jv
= value_from( pos
);
645 assert( serialize( jv
) == "{\"x\":4,\"y\":1,\"z\":4}" );
652 value jv
= value_from( 1.5 ); // error
659 std::map
< std::string
, vec3
< int > > positions
= {
660 { "Alex", { 42, -60, 18 } },
661 { "Blake", { 300, -60, -240} },
662 { "Carol", { -60, 30, 30 } }
665 // conversions are applied recursively;
666 // the key type and value type will be converted
667 // using value_from as well
668 value jv
= value_from( positions
);
670 assert( jv
.is_object() );
672 object
& jo
= jv
.as_object();
674 assert( jo
.size() == 3 );
676 // The sum of the coordinates is 0
677 assert( std::accumulate( jo
.begin(), jo
.end(), std::int64_t(0),
678 []( std::int64_t total
, const key_value_pair
& jp
)
680 assert ( jp
.value().is_object() );
682 const object
& pos
= jp
.value().as_object();
684 return total
+ pos
.at( "x" ).as_int64() +
685 pos
.at( "y" ).as_int64() +
686 pos
.at( "z" ).as_int64();
697 std::vector
< customer
> customers
= {
698 customer( 0, "Alison", false ),
699 customer( 1, "Bill", false ),
700 customer( 3, "Catherine", true ),
701 customer( 4, "Doug", false )
704 storage_ptr sp
= make_shared_resource
< monotonic_resource
>();
706 value jv
= value_from( customers
, sp
);
708 assert( jv
.storage() == sp
);
710 assert( jv
.is_array() );
718 // Satisfies both FromMapLike and FromContainerLike
719 std::unordered_map
< std::string
, bool > available_tools
= {
726 value jv
= value_from( available_tools
);
728 assert( jv
.is_object() );
735 std::complex< double > c1
= { 3.14159, 2.71828 };
737 // Convert a complex number to JSON
738 value jv
= value_from( c1
);
740 assert ( jv
.is_array() );
742 // Convert back to a complex number
744 std::complex< double > c2
= value_to
< std::complex< double > >( jv
);
753 customer
c1( 5, "Ed", false );
755 // Convert customer to value
756 value jv
= value_from( c1
);
758 // Convert the result back to customer
759 customer c2
= value_to
< customer
>( jv
);
761 // The resulting customer is unchanged
762 assert( c1
.name
== c2
.name
);
769 value available_tools
= {
776 assert( available_tools
.is_object() );
778 auto as_map
= value_to
< std::map
< std::string
, bool > >( available_tools
);
780 assert( available_tools
.as_object().size() == as_map
.size() );
787 has_value_from
<customer
>::value
);
790 has_value_from
<std::complex<float>>::value
);
792 has_value_from
<std::complex<double>>::value
);
795 has_value_to
<std::complex<float>>::value
);
797 has_value_to
<std::complex<double>>::value
);
804 //----------------------------------------------------------
809 class my_non_deallocating_resource
{ };
813 //[snippet_allocators_14
818 struct is_deallocate_trivial
< my_non_deallocating_resource
>
820 static constexpr bool value
= true;
848 TEST_SUITE(snippets_test
, "boost.json.snippets");