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>
19 #include <unordered_map>
22 #include "test_suite.hpp"
25 # pragma warning(push)
26 # pragma warning(disable: 4101)
27 #elif defined(__clang__)
28 # pragma clang diagnostic push
29 # pragma clang diagnostic ignored "-Wunused"
30 # pragma clang diagnostic ignored "-Wmismatched-tags"
31 #elif defined(__GNUC__)
32 # pragma GCC diagnostic push
33 # pragma GCC diagnostic ignored "-Wunused"
38 template< std::size_t N
>
39 struct static_string
{ };
44 template< std::size_t N
>
45 class hash
< static_string
< N
> >
49 operator()(const static_string
< N
>& str
) const noexcept
51 return std::hash
< std::string
>()( str
);
65 tag_invoke( const value_from_tag
&, value
& jv
, std::complex< T
> const& t
)
67 // Store a complex number as a 2-element array
68 // with the real part followed by the imaginary part
69 jv
= { t
.real(), t
.imag() };
74 tag_invoke( const value_to_tag
< std::complex< T
> >&, value
const& jv
)
76 return std::complex< T
>(
77 jv
.as_array().at(0).to_number
< T
>(),
78 jv
.as_array().at(1).to_number
< T
>());
91 string str1
; // empty string, uses the default memory resource
93 string
str2( make_shared_resource
<monotonic_resource
>() ); // empty string, uses a counted monotonic resource
100 std::string sstr1
= "helloworld";
101 std::string sstr2
= "world";
103 json::string jstr1
= "helloworld";
104 json::string jstr2
= "world";
106 assert( jstr2
.insert(0, jstr1
.subview(0, 5)) == "helloworld" );
108 // this is equivalent to
109 assert( sstr2
.insert(0, sstr1
, 0, 5) == "helloworld" );
116 std::string sstr
= "hello";
118 json::string jstr
= "hello";
120 assert(sstr
.append({'w', 'o', 'r', 'l', 'd'}) == "helloworld");
122 // such syntax is inefficient, and the same can
123 // be achieved with a character array.
125 assert(jstr
.append("world") == "helloworld");
133 json::string str
= "Boost.JSON";
134 json::string_view sv
= str
;
136 // all of these call compare(string_view)
139 str
.compare(sv
.substr(0, 5));
143 str
.compare("Boost");
149 //----------------------------------------------------------
158 value
jv2( nullptr );
160 assert( jv1
.is_null() );
161 assert( jv2
.is_null() );
168 value
jv( object_kind
);
170 assert( jv
.kind() == kind::object
);
171 assert( jv
.is_object() );
172 assert( ! jv
.is_number() );
180 value
jv( object_kind
);
182 if( auto p
= jv
.if_object() )
186 return std::size_t(0);
194 jv
= value( array_kind
);
196 assert( jv
.is_array() );
200 assert( jv
.is_string() );
208 jv
.emplace_string() = "Hello, world!";
210 int64_t& num
= jv
.emplace_int64();
213 assert( jv
.is_int64() );
225 jv
.as_string() = "Hello, world!"; // throws an exception
236 value
jv( string_kind
);
237 if( string
* str
= jv
.if_string() )
238 *str
= "Hello, world!";
245 value
jv( string_kind
);
247 // The compiler's static analysis can see that
248 // a null pointer is never dereferenced.
249 *jv
.if_string() = "Hello, world!";
255 //----------------------------------------------------------
261 //[snippet_init_list_1
264 { "name", "John Doe" },
266 { "associated-accounts", nullptr },
267 { "total-balance", 330.00 },
268 { "account-balances", { 84, 120, 126 } } };
274 //[snippet_init_list_2
276 value jv
= { true, 2, "hello", nullptr };
278 assert( jv
.is_array() );
280 assert( jv
.as_array().size() == 4 );
282 assert( serialize(jv
) == "[true,2,\"hello\",null]" );
288 //[snippet_init_list_3
290 value jv
= { true, 2, "hello", { "bye", nullptr, false } };
292 assert( jv
.is_array() );
294 assert( jv
.as_array().back().is_array() );
296 assert( serialize(jv
) == "[true,2,\"hello\",[\"bye\",null,false]]" );
302 //[snippet_init_list_4
304 // Should this be an array or an object?
305 value jv
= { { "hello", 42 }, { "world", 43 } };
311 //[snippet_init_list_5
313 value jv1
= { { "hello", 42 }, { "world", 43 } };
315 assert( jv1
.is_object() );
317 assert( jv1
.as_object().size() == 2 );
319 assert( serialize(jv1
) == R
"({"hello
":42,"world
":43})" );
321 // All of the following are arrays
323 value jv2
= { { "make", "Tesla" }, { "model", 3 }, "black" };
325 value jv3
= { { "library", "JSON" }, { "Boost", "C++", "Fast", "JSON" } };
327 value jv4
= { { "color", "blue" }, { 1, "red" } };
329 assert( jv2
.is_array() && jv3
.is_array() && jv4
.is_array() );
335 //[snippet_init_list_6
337 value jv
= { { "hello", 42 }, array
{ "world", 43 } };
339 assert( jv
.is_array() );
341 array
& ja
= jv
.as_array();
343 assert( ja
[0].is_array() && ja
[1].is_array());
345 assert ( serialize(jv
) == R
"([["hello
",42],["world
",43]])" );
353 //[snippet_init_list_7
355 value jv
= { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } };
357 assert( jv
.is_object() );
359 assert( serialize(jv
) == "{\"mercury\":36,\"venus\":67,\"earth\":93}" );
361 array ja
= { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } };
363 assert( serialize(ja
) == "[[\"mercury\",36],[\"venus\",67],[\"earth\",93]]" );
371 //[snippet_init_list_8
373 object jo
= { { "mercury", { { "distance", 36 } } }, { "venus", { 67, "million miles" } }, { "earth", 93 } };
375 assert( jo
["mercury"].is_object() );
377 assert( jo
["venus"].is_array() );
383 //[snippet_init_list_9
385 object jo1
= { { "john", 100 }, { "dave", 500 }, { "joe", 300 } };
387 value jv
= { { "clients", std::move(jo1
) } };
389 object
& jo2
= jv
.as_object()["clients"].as_object();
391 assert( ! jo2
.empty() && jo1
.empty() );
393 assert( serialize(jv
) == R
"({"clients
":{"john
":100,"dave
":500,"joe
":300}})" );
401 //----------------------------------------------------------
409 array arr1
; // empty array, uses the default memory resource
411 array
arr2( make_shared_resource
<monotonic_resource
>() ); // empty array, uses a counted monotonic resource
418 array
arr( { "Hello", 42, true } );
428 arr
.emplace_back( "Hello" );
429 arr
.emplace_back( 42 );
430 arr
.emplace_back( true );
436 assert( arr
[0].as_string() == "Hello" );
438 // The following line throws std::out_of_range, since the index is out of range
439 arr
.at( 3 ) = nullptr;
448 //----------------------------------------------------------
456 object obj1
; // empty object, uses the default memory resource
458 object
obj2( make_shared_resource
<monotonic_resource
>() ); // empty object, uses a counted monotonic resource
465 object
obj( {{"key1", "value1" }, { "key2", 42 }, { "key3", false }} );
474 obj
.emplace( "key1", "value1" );
475 obj
.emplace( "key2", 42 );
476 obj
.emplace( "key3", false );
486 obj
["key1"] = "value1";
490 // The following line throws std::out_of_range, since the key does not exist
500 //----------------------------------------------------------
505 void identity_swap( T
& a
, T
& b
)
507 // introduces the declaration of
508 // std::swap into this scope
512 // the overload set will contain std::swap,
513 // any declarations of swap within the enclosing
514 // namespace, and any declarations of swap within
515 // the namespaces associated with T
530 void tag_invoke( const value_from_tag
&, value
& jv
, const vec3
<T
>& vec
)
541 #ifdef BOOST_JSON_DOCS
544 template< class T
, typename
std::enable_if
<
545 std::is_floating_point
< T
>::value
>::type
* = nullptr >
546 void tag_invoke( const value_from_tag
&, value
& jv
, T t
)
548 jv
= std::llround( t
);
562 customer() = default;
564 customer( std::uint64_t i
, const std::string
& n
, bool l
)
565 : id( i
), name( n
), late( l
) { }
567 explicit customer( value
const& );
570 void tag_invoke( const value_from_tag
&, value
& jv
, customer
const& c
)
572 // Assign a JSON value
584 customer
tag_invoke( const value_to_tag
<customer
>&, const value
& jv
)
586 // at() throws if `jv` is not an object, or if the key is not found.
587 // as_uint64() will throw if the value is not an unsigned 64-bit integer.
588 std::uint64_t id
= jv
.at( "id" ).as_uint64();
590 // We already know that jv is an object from
591 // the previous call to jv.as_object() succeeding,
592 // now we use jv.get_object() which skips the
593 // check. value_to will throw if jv.kind() != kind::string
594 std::string name
= value_to
< std::string
>( jv
.get_object().at( "name" ) );
596 // id and name are constructed from JSON in the member
597 // initializer list above, but we can also use regular
598 // assignments in the body of the function as shown below.
599 // as_bool() will throw if kv.kind() != kind::bool
600 bool late
= jv
.get_object().at( "late" ).as_bool();
602 return customer(id
, name
, late
);
613 std::vector
< int > v1
{ 1, 2, 3, 4 };
615 // Convert the vector to a JSON array
616 value jv
= value_from( v1
);
618 assert( jv
.is_array() );
620 array
& ja
= jv
.as_array();
622 assert( ja
.size() == 4 );
624 for ( std::size_t i
= 0; i
< v1
.size(); ++i
)
625 assert( v1
[i
] == ja
[i
].as_int64() );
627 // Convert back to vector< int >
628 std::vector
< int > v2
= value_to
< std::vector
< int > >( jv
);
639 vec3
< int > pos
= { 4, 1, 4 };
641 value jv
= value_from( pos
);
643 assert( serialize( jv
) == "{\"x\":4,\"y\":1,\"z\":4}" );
650 value jv
= value_from( 1.5 ); // error
657 std::map
< std::string
, vec3
< int > > positions
= {
658 { "Alex", { 42, -60, 18 } },
659 { "Blake", { 300, -60, -240} },
660 { "Carol", { -60, 30, 30 } }
663 // conversions are applied recursively;
664 // the key type and value type will be converted
665 // using value_from as well
666 value jv
= value_from( positions
);
668 assert( jv
.is_object() );
670 object
& jo
= jv
.as_object();
672 assert( jo
.size() == 3 );
674 // The sum of the coordinates is 0
675 assert( std::accumulate( jo
.begin(), jo
.end(), std::int64_t(0),
676 []( std::int64_t total
, const key_value_pair
& jp
)
678 assert ( jp
.value().is_object() );
680 const object
& pos
= jp
.value().as_object();
682 return total
+ pos
.at( "x" ).as_int64() +
683 pos
.at( "y" ).as_int64() +
684 pos
.at( "z" ).as_int64();
695 std::vector
< customer
> customers
= {
696 customer( 0, "Alison", false ),
697 customer( 1, "Bill", false ),
698 customer( 3, "Catherine", true ),
699 customer( 4, "Doug", false )
702 storage_ptr sp
= make_shared_resource
< monotonic_resource
>();
704 value jv
= value_from( customers
, sp
);
706 assert( jv
.storage() == sp
);
708 assert( jv
.is_array() );
716 // Satisfies both FromMapLike and FromContainerLike
717 std::unordered_map
< std::string
, bool > available_tools
= {
724 value jv
= value_from( available_tools
);
726 assert( jv
.is_object() );
733 std::complex< double > c1
= { 3.14159, 2.71828 };
735 // Convert a complex number to JSON
736 value jv
= value_from( c1
);
738 assert ( jv
.is_array() );
740 // Convert back to a complex number
742 std::complex< double > c2
= value_to
< std::complex< double > >( jv
);
751 customer
c1( 5, "Ed", false );
753 // Convert customer to value
754 value jv
= value_from( c1
);
756 // Convert the result back to customer
757 customer c2
= value_to
< customer
>( jv
);
759 // The resulting customer is unchanged
760 assert( c1
.name
== c2
.name
);
767 value available_tools
= {
774 assert( available_tools
.is_object() );
776 auto as_map
= value_to
< std::map
< std::string
, bool > >( available_tools
);
778 assert( available_tools
.as_object().size() == as_map
.size() );
788 value jv
= { {"one", 1}, {"two", 2} };
789 assert( jv
.at("one") == jv
.at_pointer("/one") );
791 jv
.at_pointer("/one") = {{"foo", "bar"}};
792 assert( jv
.at("one").at("foo") == jv
.at_pointer("/one/foo") );
794 jv
.at_pointer("/one/foo") = {true, 4, "qwerty"};
795 assert( jv
.at("one").at("foo").at(1) == jv
.at_pointer("/one/foo/1") );
798 value
* elem1
= [&]() -> value
*
801 object
* obj
= jv
.if_object();
805 value
* val
= obj
->if_contains("one");
809 obj
= val
->if_object();
813 val
= obj
->if_contains("foo");
817 array
* arr
= val
->if_array();
821 return arr
->if_contains(1);
825 value
* elem2
= [&]() -> value
*
829 return jv
.find_pointer("/one/foo/1", ec
);
835 assert( elem1
== elem2
);
839 has_value_from
<customer
>::value
);
842 has_value_from
<std::complex<float>>::value
);
844 has_value_from
<std::complex<double>>::value
);
847 has_value_to
<std::complex<float>>::value
);
849 has_value_to
<std::complex<double>>::value
);
856 //----------------------------------------------------------
861 class my_non_deallocating_resource
{ };
865 //[snippet_allocators_14
870 struct is_deallocate_trivial
< my_non_deallocating_resource
>
872 static constexpr bool value
= true;
901 TEST_SUITE(snippets_test
, "boost.json.snippets");