The C++ Boost Library: Beyond the STL
Overview
Boost.JSON is a portable C++ library which provides containers and algorithms that implement JavaScript Object Notation, (JSON) a lightweight data-interchange format.
This library focuses on a common and popular use-case: parsing and serializing to and from a container called value which holds JSON types. Any value which you build can be serialized and then deserialized, guaranteeing that the result will be equal to the original value. Whatever JSON output you produce with this library will be readable by most common JSON implementations in any language.
The value container is designed to be well suited as a vocabulary type appropriate for use in public interfaces and libraries, allowing them to be composed. The library restricts the representable data types to the ranges which are almost universally accepted by most JSON implementations, especially JavaScript. The parser and serializer are both highly performant, meeting or exceeding the benchmark performance of the best comparable libraries. Allocators are very well supported. Code which uses these types will be easy to understand, flexible, and performant.
Boost.JSON offers these features:
- Fast compilation
- Require only C++11
- Fast streaming parser and serializer
- Constant-time key lookup for objects
- Options to allow non-standard JSON
- Easy and safe modern API with allocator support
- Optional header-only, without linking to a library
To use as header-only; that is, to eliminate the requirement to link a program to a static or dynamic Boost.JSON library, simply place the following line in exactly one new or existing source file in your project:
#include <boost/json/src.hpp>
The library relies heavily on these well known C++ types in its interfaces (henceforth termed standard types):
- string_view
- memory_resource, polymorphic_allocator
- error_category, error_code, error_condition, system_error
boost::json::object
and boost::json::object
In this library the types array
, object
, and string
hold JSON arrays, objects, and strings respectively while the type value is a special variant which can hold any JSON element. Here we construct an empty object and then insert the elements above:
object obj; // construct an empty object obj[ "pi" ] = 3.141; // insert a double obj[ "happy" ] = true; // insert a bool obj[ "name" ] = "Boost"; // insert a string obj[ "nothing" ] = nullptr; // insert a null obj[ "answer" ].emplace_object()["everything"] = 42; // insert an object with 1 element obj[ "list" ] = { 1, 0, 2 }; // insert an array with 3 elements obj[ "object" ] = { {"currency", "USD"}, {"value", 42.99} }; // insert an object with 2 elements
boost::json::value
While keys are strings, the mapped type of objects and the element type of arrays is a special type called value
which can hold any JSON element, as seen in the previous assignments. Instead of building the JSON document using a series of function calls, we can build it in one statement using an initializer list:
value jv = { { "pi", 3.141 }, { "happy", true }, { "name", "Boost" }, { "nothing", nullptr }, { "answer", { { "everything", 42 } } }, {"list", {1, 0, 2}}, {"object", { { "currency", "USD" }, { "value", 42.99 } } } };
...
Serializing
Serialization is the process where a JSON document represented in memory by a value
is turned into a sequence of characters. The library provides the following free functions and types for serialization:
Name | Description |
operator<< | Serialize a value, array, object, or string to a std::ostream . |
serialize | Return a std::string representing a serialized value , array , object, or string. |
serializer | A stateful object which may be used to efficiently serialize one or more instances of value, array, object, or string. |
To facilitate debugging and ease of output, library container types may be written to standard output streams using the stream operator:
value jv = { 1, 2, 3, 4, 5 }; std::cout << jv << "\n";
The serialize
function converts a value into a std::string
:
value jv = { 1, 2, 3, 4, 5 }; std::string s = serialize( jv );
Implementing operator<<
Using a serializer
In situations where serializing a value in its entirety is inefficient or even impossible, serializer can be used to incrementally serialize a value incrementally. This may be done for a variety of reasons, such as to avoid buffering the entire output, or to ensure that a fixed amount of work is performed in each cycle. Instances of serializer maintain an output state using internal dynamically allocated structures, with an interface to retrieve successive buffers of the serialized output into a caller provided buffer. Here is an example, demonstrating how operator<<
may be implemented using a serializer
:
// Serialize a value into an output stream std::ostream& operator<<( std::ostream& os, value const& jv ) { // Create a serializer serializer sr; // Set the serializer up for our value sr.reset( &jv ); // Loop until all output is produced. while( ! sr.done() ) { // Use a local buffer to avoid allocation. char buf[ BOOST_JSON_STACK_BUFFER_SIZE ]; // Fill our buffer with serialized characters and write it to the output stream. os << sr.read( buf ); } return os; }
As with the parser, the serializer may be reused by calling serializer::reset. This sets up the object to serialize a new instance and retains previously allocated memory. This can result in performance improvements when multiple variables are serialized.