// // 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/boostorg/json // #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4101) # pragma warning(disable: 4996) #elif defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunused" # pragma clang diagnostic ignored "-Wmismatched-tags" #elif defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused" #endif #include #include #include #include #include #include #include #include "test_suite.hpp" #include "doc_types.hpp" //[snippet_conv_spec_trait2 namespace boost { namespace json { template<> struct is_sequence_like< user_ns::ip_address > : std::false_type { }; } // namespace json } // namespace boost //] namespace user_ns2 { class ip_address : public user_ns::ip_address { public: using user_ns::ip_address::ip_address; }; using namespace boost::json; //[snippet_tag_invoke_1 void tag_invoke( const value_from_tag&, value& jv, ip_address const& addr ) { // Store the IP address as a 4-element array of octets const unsigned char* b = addr.begin(); jv = { b[0], b[1], b[2], b[3] }; } ip_address tag_invoke( const value_to_tag< ip_address >&, value const& jv ) { array const& arr = jv.as_array(); return ip_address( arr.at(0).to_number< unsigned char >(), arr.at(1).to_number< unsigned char >(), arr.at(2).to_number< unsigned char >(), arr.at(3).to_number< unsigned char >() ); } //] //[snippet_nothrow_1 result_for< ip_address, value >::type tag_invoke( const try_value_to_tag< ip_address >&, value const& jv ) { if( !jv.is_array() ) return make_error_code( std::errc::invalid_argument ); array const& arr = jv.get_array(); if( arr.size() != 4 ) return make_error_code( std::errc::invalid_argument ); result< unsigned char > oct1 = try_value_to< unsigned char >( arr[0] ); if( !oct1 ) return make_error_code( std::errc::invalid_argument ); result< unsigned char > oct2 = try_value_to< unsigned char >( arr[1] ); if( !oct2 ) return make_error_code( std::errc::invalid_argument ); result< unsigned char > oct3 = try_value_to< unsigned char >( arr[2] ); if( !oct3 ) return make_error_code( std::errc::invalid_argument ); result< unsigned char > oct4 = try_value_to< unsigned char >( arr[3] ); if( !oct4 ) return make_error_code( std::errc::invalid_argument ); return ip_address{ *oct1, *oct2, *oct3, *oct4 }; } //] } // namespace user_ns namespace boost { namespace json { namespace { //[snippet_strings_5 string greeting( string_view first_name, string_view last_name ) { const char hello[] = "Hello, "; const std::size_t sz = first_name.size() + last_name.size() + sizeof(hello) + 1; string js; js.reserve(sz); char* p = std::copy( hello, hello + sizeof(hello) - 1, js.data() ); p = std::copy( first_name.begin(), first_name.end(), p ); *p++ = ' '; p = std::copy( last_name.begin(), last_name.end(), p ); *p++ = '!'; js.grow( sz ); return js; } //] void usingStrings() { { //[snippet_strings_1 string str1; // empty string, uses the default memory resource string str2( make_shared_resource() ); // empty string, uses a counted monotonic resource //] } { //[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, 5)) == "helloworld" ); // this is equivalent to assert( sstr2.insert(0, sstr1, 0, 5) == "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"); //] } { auto str = greeting( "John", "Smith" ); (void)str; assert( str == "Hello, John Smith!"); } } //---------------------------------------------------------- void usingValues() { { //[snippet_value_1 value jv1; value jv2( nullptr ); assert( jv1.is_null() ); assert( jv2.is_null() ); //] } { //[snippet_value_2 value jv( object_kind ); assert( jv.kind() == kind::object ); assert( jv.is_object() ); assert( ! jv.is_number() ); //] } { auto f = []{ //[snippet_value_3 value jv( object_kind ); if( auto p = jv.if_object() ) return p->size(); //] return std::size_t(0); }; (void)f; } { //[snippet_value_4 value jv; jv = value( array_kind ); assert( jv.is_array() ); jv.emplace_string(); assert( jv.is_string() ); //] } { //[snippet_value_5 value jv; jv.emplace_string() = "Hello, world!"; int64_t& num = jv.emplace_int64(); num = 1; assert( jv.is_int64() ); //] } { try { //[snippet_value_6 value jv( true ); jv.as_bool() = true; jv.as_string() = "Hello, world!"; // throws an exception //] } catch(...) { } } { //[snippet_value_7 value jv( string_kind ); if( string* str = jv.if_string() ) *str = "Hello, world!"; //] } { //[snippet_value_8 value jv( string_kind ); // The compiler's static analysis can see that // a null pointer is never dereferenced. *jv.if_string() = "Hello, world!"; //] } } //---------------------------------------------------------- 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 } } }; //] } { //[snippet_init_list_2 value jv = { true, 2, "hello", nullptr }; assert( jv.is_array() ); assert( jv.as_array().size() == 4 ); assert( serialize(jv) == R"([true,2,"hello",null])" ); //] } { //[snippet_init_list_3 value jv = { true, 2, "hello", { "bye", nullptr, false } }; assert( jv.is_array() ); assert( jv.as_array().back().is_array() ); assert( serialize(jv) == R"([true,2,"hello",["bye",null,false]])" ); //] } { //[snippet_init_list_4 // Should this be an array or an object? value jv = { { "hello", 42 }, { "world", 43 } }; //] } { //[snippet_init_list_5 value jv1 = { { "hello", 42 }, { "world", 43 } }; assert( jv1.is_object() ); assert( jv1.as_object().size() == 2 ); assert( serialize(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() ); //] } { //[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 ( serialize(jv) == R"([["hello",42],["world",43]])" ); //] (void)ja; } { //[snippet_init_list_7 value jv = { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } }; assert( jv.is_object() ); assert( serialize(jv) == R"({"mercury":36,"venus":67,"earth":93})" ); array ja = { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } }; assert( serialize(ja) == R"([["mercury",36],["venus",67],["earth",93]])" ); //] (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() ); //] } { //[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( serialize(jv) == R"({"clients":{"john":100,"dave":500,"joe":300}})" ); //] (void)jo2; } } //---------------------------------------------------------- void usingArrays() { { //[snippet_arrays_1 array arr1; // empty array, uses the default memory resource array arr2( make_shared_resource() ); // empty array, uses a counted monotonic resource //] } { //[snippet_arrays_2 array arr( { "Hello", 42, true } ); //] } try { //[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; //] } catch (...) { } } //---------------------------------------------------------- void usingObjects() { { //[snippet_objects_1 object obj1; // empty object, uses the default memory resource object obj2( make_shared_resource() ); // empty object, uses a counted monotonic resource //] } { //[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 ); //] } try { //[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" ); //] } catch (...) { } { //[snippet_objects_5 object obj{{"arr", {1, 11}}}; value& arr = obj.at("arr"); obj.emplace("added", "value"); // invalidates arr //] (void)arr; } } //[snippet_conv_5 template< class T > struct vec3 { T x, y, z; }; template< class T > void tag_invoke( const value_from_tag&, value& jv, const vec3& vec ) { jv = { { "x", vec.x }, { "y", vec.y }, { "z", vec.z } }; } //] //[snippet_conv_10 struct customer { std::uint64_t id; std::string name; bool late; customer() = default; customer( std::uint64_t i, const std::string& n, bool l ) : id( i ), name( n ), late( l ) { } }; void tag_invoke( const value_from_tag&, value& jv, customer const& c ) { // Assign a JSON value jv = { { "id", c.id }, { "name", c.name }, { "late", c.late } }; } //] //[snippet_conv_14 customer tag_invoke( const value_to_tag&, const value& 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. std::uint64_t id = jv.at( "id" ).as_uint64(); // We already know that jv is an object from // the previous call to jv.as_object() succeeding, // now we use jv.get_object() which skips the // check. value_to will throw if jv.kind() != kind::string std::string name = value_to< 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 bool late = jv.get_object().at( "late" ).as_bool(); return customer(id, name, late); } //] void usingExchange() { { //[snippet_conv_1 std::vector< int > v1{ 1, 2, 3, 4 }; // Convert the vector to a JSON array value jv = value_from( v1 ); assert( serialize( jv ) == R"([1,2,3,4])" ); // Convert back to vector< int > std::vector< int > v2 = value_to< std::vector< int > >( jv ); assert( v1 == v2 ); //] (void)v2; } { using namespace user_ns2; //[snippet_tag_invoke_3 std::map< std::string, ip_address > computers = { { "Alex", { 192, 168, 1, 1 } }, { "Blake", { 192, 168, 1, 2 } }, { "Carol", { 192, 168, 1, 3 } }, }; // conversions are applied recursively; // the key type and value type will be converted // using value_from as well value jv = value_from( computers ); assert( jv.is_object() ); value serialized = parse(R"( { "Alex": [ 192, 168, 1, 1 ], "Blake": [ 192, 168, 1, 2 ], "Carol": [ 192, 168, 1, 3 ] } )"); assert( jv == serialized ); //] (void)jv; } { using namespace user_ns2; //[snippet_tag_invoke_2 ip_address addr = { 127, 0, 0, 12 }; value jv = value_from( addr ); assert( serialize( jv ) == R"([127,0,0,12])" ); // Convert back to IP address ip_address addr2 = value_to< ip_address >( jv ); assert(std::equal( addr.begin(), addr.end(), addr2.begin() )); //] (void)addr2; } { using namespace user_ns2; //[snippet_nothrow_2 value jv = parse( R"([127,0,0,12])" ); result< ip_address > addr = try_value_to< ip_address >( jv ); assert( addr.has_value() ); ip_address addr2{ 127, 0, 0, 12 }; assert(std::equal( addr->begin(), addr->end(), addr2.begin() )); // this fails without exception addr = try_value_to< ip_address >( value() ); assert( addr.has_error() ); //] (void)addr; (void)addr2; } { //[snippet_conv_recursive std::map< std::string, std::pair > m = { {"a", {1, false}}, {"b", {4, true}}, {"c", {5, false}}, }; value jv = value_from( m ); assert(( jv == object{ {"a", array{1, false}}, {"b", array{4, true}}, {"c", array{5, false}}, })); //] } } void usingPointer() { //[snippet_pointer_1 value jv = { {"one", 1}, {"two", 2} }; assert( jv.at("one") == jv.at_pointer("/one") ); jv.at_pointer("/one") = {{"foo", "bar"}}; assert( jv.at("one").at("foo") == jv.at_pointer("/one/foo") ); jv.at_pointer("/one/foo") = {true, 4, "qwerty"}; assert( jv.at("one").at("foo").at(1) == jv.at_pointer("/one/foo/1") ); //] value* elem1 = [&]() -> value* { //[snippet_pointer_2 object* obj = jv.if_object(); if( !obj ) return nullptr; value* val = obj->if_contains("one"); if( !val ) return nullptr; obj = val->if_object(); if( !obj ) return nullptr; val = obj->if_contains("foo"); if( !val ) return nullptr; array* arr = val->if_array(); if( !arr ) return nullptr; return arr->if_contains(1); //] }(); value* elem2 = [&]() -> value* { //[snippet_pointer_3 error_code ec; return jv.find_pointer("/one/foo/1", ec); //] }(); (void)elem1; (void)elem2; assert( elem1 == elem2 ); } void usingSetAtPointer() { //[snippet_pointer_4 value jv; jv.set_at_pointer("/two/bar/0", 12); assert( jv.is_object() ); assert( jv.at_pointer("/two").is_object() ); assert( jv.at_pointer("/two/bar").is_array() ); assert( jv.at_pointer("/two/bar/0") == 12 ); //] jv = nullptr; //[snippet_pointer_5 set_pointer_options opts; opts.create_arrays = false; jv.set_at_pointer("/two/bar/0", 12, opts); assert( jv.is_object() ); assert( jv.at_pointer("/two").is_object() ); assert( jv.at_pointer("/two/bar").is_object() ); // object, not array assert( jv.at_pointer("/two/bar/0") == 12 ); //] } BOOST_STATIC_ASSERT( has_value_from::value); BOOST_STATIC_ASSERT( has_value_from::value); BOOST_STATIC_ASSERT( has_value_to::value); } // (anon) } // json } // boost //---------------------------------------------------------- namespace { class my_non_deallocating_resource { }; } // (anon) //[snippet_allocators_14 namespace boost { namespace json { template<> struct is_deallocate_trivial< my_non_deallocating_resource > { static constexpr bool value = true; }; } // json } // boost //] namespace boost { namespace json { namespace { void usingSpecializedTrait() { value jv1{127, 0, 0, 1}; auto const addr = value_to< user_ns::ip_address >( jv1 ); auto const jv2 = value_from(addr); assert( jv1 == jv2 ); } } // namespace class snippets_test { public: void run() { usingValues(); usingInitLists(); usingExchange(); usingArrays(); usingObjects(); usingStrings(); usingPointer(); usingSpecializedTrait(); usingSetAtPointer(); BOOST_TEST_PASS(); } }; TEST_SUITE(snippets_test, "boost.json.snippets"); } // namespace json } // namespace boost