## Examples

## Pretty

    /*
        This example parses a JSON file and pretty-prints
        it to standard output.
    */

    #include <boost/json.hpp>
    #include <iomanip>
    #include <iostream>

    #include "file.hpp"

    namespace json = boost::json;

    json::value
    parse_file( char const* filename )
    {
        file f( filename, "r" );
        json::stream_parser p;
        boost::system::error_code ec;
        do
        {
            char buf[4096];
            auto const nread = f.read( buf, sizeof(buf) );
            p.write( buf, nread, ec );
        }
        while( ! f.eof() );
        if( ec )
            return nullptr;
        p.finish( ec );
        if( ec )
            return nullptr;
        return p.release();
    }

    void
    pretty_print( std::ostream& os, json::value const& jv, std::string* indent = nullptr )
    {
        std::string indent_;
        if(! indent)
            indent = &indent_;
        switch(jv.kind())
        {
        case json::kind::object:
        {
            os << "{\n";
            indent->append(4, ' ');
            auto const& obj = jv.get_object();
            if(! obj.empty())
            {
                auto it = obj.begin();
                for(;;)
                {
                    os << *indent << json::serialize(it->key()) << " : ";
                    pretty_print(os, it->value(), indent);
                    if(++it == obj.end())
                        break;
                    os << ",\n";
                }
            }
            os << "\n";
            indent->resize(indent->size() - 4);
            os << *indent << "}";
            break;
        }

        case json::kind::array:
        {
            os << "[\n";
            indent->append(4, ' ');
            auto const& arr = jv.get_array();
            if(! arr.empty())
            {
                auto it = arr.begin();
                for(;;)
                {
                    os << *indent;
                    pretty_print( os, *it, indent);
                    if(++it == arr.end())
                        break;
                    os << ",\n";
                }
            }
            os << "\n";
            indent->resize(indent->size() - 4);
            os << *indent << "]";
            break;
        }

        case json::kind::string:
        {
            os << json::serialize(jv.get_string());
            break;
        }

        case json::kind::uint64:
        case json::kind::int64:
        case json::kind::double_:
            os << jv;
            break;

        case json::kind::bool_:
            if(jv.get_bool())
                os << "true";
            else
                os << "false";
            break;

        case json::kind::null:
            os << "null";
            break;
        }

        if(indent->empty())
            os << "\n";
    }

    int
    main(int argc, char** argv)
    {
        if(argc != 2)
        {
            std::cerr <<
                "Usage: pretty <filename>"
                << std::endl;
            return EXIT_FAILURE;
        }

        try
        {
            // Parse the file as JSON
            auto const jv = parse_file( argv[1] );

            // Now pretty-print the value
            pretty_print(std::cout, jv);
        }
        catch(std::exception const& e)
        {
            std::cerr <<
                "Caught exception: "
                << e.what() << std::endl;
            return EXIT_FAILURE;
        }

        return EXIT_SUCCESS;
    }

## Validate

    /*
        This example verifies that a file contains valid JSON.
    */

    #include <boost/json.hpp>

    // This file must be manually included when
    // using basic_parser to implement a parser.
    #include <boost/json/basic_parser_impl.hpp>

    #include <iomanip>
    #include <iostream>

    #include "file.hpp"

    using namespace boost::json;

    // The null parser discards all the data
    class null_parser
    {
        struct handler
        {
            constexpr static std::size_t max_object_size = std::size_t(-1);
            constexpr static std::size_t max_array_size = std::size_t(-1);
            constexpr static std::size_t max_key_size = std::size_t(-1);
            constexpr static std::size_t max_string_size = std::size_t(-1);

            bool on_document_begin( boost::system::error_code& ) { return true; }
            bool on_document_end( boost::system::error_code& ) { return true; }
            bool on_object_begin( boost::system::error_code& ) { return true; }
            bool on_object_end( std::size_t, boost::system::error_code& ) { return true; }
            bool on_array_begin( boost::system::error_code& ) { return true; }
            bool on_array_end( std::size_t, boost::system::error_code& ) { return true; }
            bool on_key_part( string_view, std::size_t, boost::system::error_code& ) { return true; }
            bool on_key( string_view, std::size_t, boost::system::error_code& ) { return true; }
            bool on_string_part( string_view, std::size_t, boost::system::error_code& ) { return true; }
            bool on_string( string_view, std::size_t, boost::system::error_code& ) { return true; }
            bool on_number_part( string_view, boost::system::error_code& ) { return true; }
            bool on_int64( std::int64_t, string_view, boost::system::error_code& ) { return true; }
            bool on_uint64( std::uint64_t, string_view, boost::system::error_code& ) { return true; }
            bool on_double( double, string_view, boost::system::error_code& ) { return true; }
            bool on_bool( bool, boost::system::error_code& ) { return true; }
            bool on_null( boost::system::error_code& ) { return true; }
            bool on_comment_part(string_view, boost::system::error_code&) { return true; }
            bool on_comment(string_view, boost::system::error_code&) { return true; }
        };

        basic_parser<handler> p_;

    public:
        null_parser()
            : p_(parse_options())
        {
        }

        ~null_parser()
        {
        }

        std::size_t
        write(
            char const* data,
            std::size_t size,
            boost::system::error_code& ec)
        {
            auto const n = p_.write_some( false, data, size, ec );
            if(! ec && n < size)
                ec = error::extra_data;
            return n;
        }
    };

    bool
    validate( string_view s )
    {
        // Parse with the null parser and return false on error
        null_parser p;
        boost::system::error_code ec;
        p.write( s.data(), s.size(), ec );
        if( ec )
            return false;

        // The string is valid JSON.
        return true;
    }

    int
    main(int argc, char** argv)
    {
        if(argc != 2)
        {
            std::cerr <<
                "Usage: validate <filename>"
                << std::endl;
            return EXIT_FAILURE;
        }

        try
        {
            // Read the file into a string
            auto const s = read_file( argv[1] );

            // See if the string is valid JSON
            auto const valid = validate( s );

            // Print the result
            if( valid )
                std::cout << argv[1] << " contains a valid JSON\n";
            else
                std::cout << argv[1] << " does not contain a valid JSON\n";
        }
        catch(std::exception const& e)
        {
            std::cerr <<
                "Caught exception: "
                << e.what() << std::endl;
            return EXIT_FAILURE;
        }

        return EXIT_SUCCESS;
    }

## Allocator-Aware Conversion

    /*
        This example uses a context that stores an allocator to create sequences during conversions
    */

    #include <boost/container/pmr/monotonic_buffer_resource.hpp>
    #include <boost/container/pmr/vector.hpp>
    #include <boost/json.hpp>
    #include <boost/system/errc.hpp>

    using namespace boost::json;
    using namespace boost::container;

    template< class Alloc >
    struct use_allocator_t
    {
        Alloc allocator;
    };

    template< class Alloc >
    use_allocator_t< Alloc >
    use_allocator( Alloc alloc ) noexcept
    {
        return { alloc };
    }

    template<
        class T,
        class Alloc,
        class FullContext,
        class = typename std::enable_if<
            is_sequence_like<T>::value &&
            std::uses_allocator<T, Alloc>::value
            >::type >
    boost::system::result<T>
    tag_invoke( try_value_to_tag<T>, const value& jv, const use_allocator_t<Alloc>& ctx, const FullContext& full_ctx )
    {

        array const* arr = jv.if_array();
        if( !arr )
            return {
                boost::system::in_place_error,
                make_error_code(boost::system::errc::invalid_argument) };

        T result(ctx.allocator);
        auto ins = std::inserter(result, result.end());
        for( value const& val: *arr )
        {
            using ValueType = typename T::value_type;
            auto elem_res = try_value_to<ValueType>( val, full_ctx );
            if( elem_res.has_error() )
                return {boost::system::in_place_error, elem_res.error()};
            *ins++ = std::move(*elem_res);
        }
        return result;
    }

    int
    main(int, char**)
    {

        value const jv = { 1, 2, 3, 4, 5, 6, 7, 8 };

        unsigned char buf[1024];
        pmr::monotonic_buffer_resource mr( buf, sizeof(buf) );

        auto v = value_to< pmr::vector<int> >(
            jv,
            use_allocator( pmr::polymorphic_allocator<int>(&mr) ) );

        assert( v.size() == jv.as_array().size() );

        for( auto i = 0u; i < v.size(); ++i )
            assert( v[i] == jv.at(i).to_number<int>() );

        return EXIT_SUCCESS;
    }

## CBOR

    /*
        This example implements simple parsing and serialization of a
        subset of CBOR types that are directly supported by JSON.
    */

    #include <boost/json.hpp>
    #include <boost/endian.hpp>
    #include <stdexcept>
    #include <fstream>
    #include <iostream>

    using namespace boost::json;

    void serialize_cbor_number(
        unsigned char mt, std::uint64_t n, std::vector<unsigned char> & out )
    {
        mt <<= 5;

        if( n < 24 )
        {
            out.push_back( static_cast<unsigned char>( mt + n ) );
        }
        else if( n < 256 )
        {
            unsigned char data[] = { static_cast<unsigned char>( mt + 24 ), static_cast<unsigned char>( n ) };
            out.insert( out.end(), std::begin( data ), std::end( data ) );
        }
        else if( n < 65536 )
        {
            unsigned char data[] = { static_cast<unsigned char>( mt + 25 ), static_cast<unsigned char>( n >> 8 ), static_cast<unsigned char>( n ) };
            out.insert( out.end(), std::begin( data ), std::end( data ) );
        }
        else if( n < 0x1000000ull )
        {
            unsigned char data[ 5 ];

            data[ 0 ] = static_cast<unsigned char>( mt + 26 );
            boost::endian::endian_store<std::uint32_t, 4, boost::endian::order::big>( data + 1, static_cast<std::uint32_t>( n ) );

            out.insert( out.end(), std::begin( data ), std::end( data ) );
        }
        else
        {
            unsigned char data[ 9 ];

            data[ 0 ] = static_cast<unsigned char>( mt + 27 );
            boost::endian::endian_store<std::uint64_t, 8, boost::endian::order::big>( data + 1, n );

            out.insert( out.end(), std::begin( data ), std::end( data ) );
        }
    }

    void
    serialize_cbor_string( string_view sv, std::vector<unsigned char>& out )
    {
        std::size_t n = sv.size();
        serialize_cbor_number( 3, n, out );

        out.insert( out.end(), sv.data(), sv.data() + n );
    }

    void
    serialize_cbor_value( const value& jv, std::vector<unsigned char>& out )
    {
        switch( jv.kind() )
        {
        case kind::null:
            out.push_back( 224 + 22 );
            break;

        case kind::bool_:
            out.push_back( 224 + 20 + jv.get_bool() );
            break;

        case kind::int64:
            {
                std::int64_t n = jv.get_int64();
                if( n >= 0 )
                    serialize_cbor_number( 0, n, out );
                else
                    serialize_cbor_number( 1, ~n, out );
            }
            break;

        case kind::uint64:
            serialize_cbor_number( 0, jv.get_uint64(), out );
            break;

        case kind::double_:
            {
                unsigned char data[ 9 ];
                data[ 0 ] = 224 + 27;
                boost::endian::endian_store<double, 8, boost::endian::order::big>( data + 1, jv.get_double() );

                out.insert( out.end(), std::begin(data), std::end(data) );
            }
            break;

        case kind::string:
            serialize_cbor_string( jv.get_string(), out );
            break;

        case kind::array:
            {
                const array& ja = jv.get_array();
                std::size_t n = ja.size();

                out.reserve( out.size() + n + 1 );

                serialize_cbor_number( 4, n, out );

                for( std::size_t i = 0; i < n; ++i )
                    serialize_cbor_value( ja[i], out );
            }
            break;

        case kind::object:
            {
                const object& jo = jv.get_object();
                std::size_t n = jo.size();

                out.reserve( out.size() + 3 * n + 1 );

                serialize_cbor_number( 5, n, out );

                for( const key_value_pair& kv: jo )
                {
                    serialize_cbor_string( kv.key(), out );
                    serialize_cbor_value( kv.value(), out );
                }
            }
            break;
        }
    }

    BOOST_NORETURN
    void
    throw_eof_error()
    {
        throw std::runtime_error( "Unexpected end of input" );
    }

    BOOST_NORETURN
    void
    throw_format_error( char const * err )
    {
        throw std::runtime_error( err );
    }

    void
    ensure( std::size_t n, const unsigned char* first, const unsigned char* last )
    {
        if( static_cast<std::size_t>(last - first) < n )
            throw_eof_error();
    }

    const unsigned char*
    parse_cbor_value(
        const unsigned char* first, const unsigned char* last, value& v );

    const unsigned char*
    parse_cbor_number(
        const unsigned char* first, const unsigned char* last, unsigned char ch, std::uint64_t& n )
    {
        unsigned char cv = ch & 31;

        if( cv < 24 )
        {
            n = cv;
        }
        else if( cv == 24 )
        {
            ensure( 1, first, last );
            n = *first++;
        }
        else if( cv == 25 )
        {
            ensure( 2, first, last );
            n = boost::endian::load_big_u16( first );
            first += 2;
        }
        else if( cv == 26 )
        {
            ensure( 4, first, last );
            n = boost::endian::load_big_u32( first );
            first += 4;
        }
        else if( cv == 27 )
        {
            ensure( 8, first, last );
            n = boost::endian::load_big_u64( first );
            first += 8;
        }
        else if( cv == 31 )
        {
            // infinite array/object
            throw_format_error( "Infinite sequences aren't supported" );
        }
        else
        {
            throw_format_error( "Invalid minor type" );
        }

        return first;
    }

    const unsigned char*
    parse_cbor_string(
        const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
    {
        std::uint64_t n;
        first = parse_cbor_number( first, last, ch, n );

        ensure( n, first, last );

        string_view sv( reinterpret_cast<char const*>( first ), n );
        first += n;

        v = sv;
        return first;
    }

    const unsigned char*
    parse_cbor_array(
        const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
    {
        std::uint64_t n;
        first = parse_cbor_number( first, last, ch, n );

        array & a = v.emplace_array();

        a.resize( n );

        std::size_t i = 0;

        for( ; i < n; ++i ) // double[] fast path
        {
            ensure( 1, first, last );
            unsigned char ch2 = *first;

            if( ch2 != 0xFB ) break;

            ++first;
            ensure( 8, first, last );

            double w = boost::endian::endian_load<double, 8, boost::endian::order::big>( first );
            first += 8;

            a[ i ] = w;
        }

        for( ; i < n; ++i ) // int[] fast path
        {
            ensure( 1, first, last );
            unsigned char ch2 = *first;

            if( ch2 >= 0x40 ) break;

            ++first;

            std::uint64_t m;
            first = parse_cbor_number( first, last, ch2, m );

            if( ch2 < 0x20 )
            {
                a[ i ] = m;
            }
            else
            {
                a[ i ] = static_cast<std::int64_t>( ~m );
            }
        }

        for( ; i < n; ++i )
            first = parse_cbor_value( first, last, a[ i ] );

        return first;
    }

    const unsigned char*
    parse_cbor_object(
        const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
    {
        std::uint64_t n;
        first = parse_cbor_number( first, last, ch, n );

        object & o = v.emplace_object();

        o.reserve( n );

        for( std::size_t i = 0; i < n; ++i )
        {
            // key string

            ensure( 1, first, last );
            unsigned char ch2 = *first++;

            if( ( ch2 >> 5 ) != 3 )
                throw_format_error( "Object keys must be strings" );

            std::uint64_t m;
            first = parse_cbor_number( first, last, ch2, m );

            ensure( m, first, last );

            string_view sv( reinterpret_cast<char const*>( first ), m );
            first += m;

            // value

            first = parse_cbor_value( first, last, o[ sv ] );
        }

        return first;
    }

    const unsigned char*
    parse_cbor_unsigned(
        const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
    {
        std::uint64_t n;
        first = parse_cbor_number( first, last, ch, n );

        v = n;
        return first;
    }

    const unsigned char*
    parse_cbor_signed(
        const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
    {
        std::uint64_t n;
        first = parse_cbor_number( first, last, ch, n );

        v = static_cast<std::int64_t>( ~n );
        return first;
    }

    const unsigned char*
    parse_cbor_semantic_tag(
        const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
    {
        std::uint64_t n;
        first = parse_cbor_number( first, last, ch, n );

        // ignore semantic tags

        return parse_cbor_value( first, last, v );
    }

    const unsigned char*
    parse_cbor_type7(
        const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
    {
        switch( ch & 31 )
        {
        case 20:
            v = false;
            return first;

        case 21:
            v = true;
            return first;

        case 22:
            v = nullptr;
            return first;

        case 26: // float
        {
            ensure( 4, first, last );

            float w = boost::endian::endian_load<float, 4, boost::endian::order::big>( first );
            first += 4;

            v = w;
            return first;
        }

        case 27: // double
        {
            ensure( 8, first, last );

            double w = boost::endian::endian_load<double, 8, boost::endian::order::big>( first );
            first += 8;

            v = w;
            return first;
        }

        default:
            throw_format_error( "Invalid minor type for major type 7" );
        }
    }

    const unsigned char*
    parse_cbor_value( const unsigned char* first, const unsigned char* last, value& v )
    {
        ensure( 1, first, last );
        const unsigned char ch = *first++;

        switch( ch >> 5 )
        {
        case 0:
            return parse_cbor_unsigned( first, last, ch, v );

        case 1:
            return parse_cbor_signed( first, last, ch, v );

        case 2:
            throw_format_error( "Binary strings aren't supported" );

        case 3:
            return parse_cbor_string( first, last, ch, v );

        case 4:
            return parse_cbor_array( first, last, ch, v );

        case 5:
            return parse_cbor_object( first, last, ch, v );

        case 6:
            return parse_cbor_semantic_tag( first, last, ch, v );

        case 7:
            return parse_cbor_type7( first, last, ch, v );

        default:
            BOOST_JSON_UNREACHABLE();
        }
    }

    int
    main(int argc, const char** argv)
    {
        if( argc != 2 )
        {
            std::cerr << "Usage: cbor FILE_NAME\n";
            return EXIT_FAILURE;
        }

        std::ifstream is(argv[1]);
        is.exceptions(std::ios::badbit);

        const value jv = parse(is);

        std::vector<unsigned char> out;
        serialize_cbor_value( jv, out );

        value jv2;
        parse_cbor_value( out.data(), out.data() + out.size(), jv2 );

        if( jv != jv2 )
        {
            std::cerr << "Roundtrip check failed\n";
            return EXIT_FAILURE;
        }

        return EXIT_SUCCESS;
    }

Edit this page