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;
}