Skip to content
Snippets Groups Projects
json.hpp 490 KiB
Newer Older
///////////////
// from_json //
///////////////

// overloads for basic_json template parameters
template <
    typename BasicJsonType, typename ArithmeticType,
    enable_if_t<std::is_arithmetic<ArithmeticType>::value and
                    not std::is_same<ArithmeticType,
                                     typename BasicJsonType::boolean_t>::value,
                int> = 0>
void get_arithmetic_value(const BasicJsonType &j, ArithmeticType &val)
{
    switch (static_cast<value_t>(j))
    {
    case value_t::number_unsigned:
    {
        val = static_cast<ArithmeticType>(
            *j.template get_ptr<
                const typename BasicJsonType::number_unsigned_t *>());
        break;
    }
    case value_t::number_integer:
    {
        val = static_cast<ArithmeticType>(
            *j.template get_ptr<
                const typename BasicJsonType::number_integer_t *>());
        break;
    }
    case value_t::number_float:
    {
        val = static_cast<ArithmeticType>(
            *j.template get_ptr<
                const typename BasicJsonType::number_float_t *>());
        break;
    }
    default:
    {
        JSON_THROW(type_error::create(302, "type must be number, but is " +
                                               j.type_name()));
    }
    }
}
template <typename BasicJsonType>
void from_json(const BasicJsonType &j, typename BasicJsonType::boolean_t &b)
{
    if (not j.is_boolean())
    {
        JSON_THROW(type_error::create(302, "type must be boolean, but is " +
                                               j.type_name()));
    }
    b = *j.template get_ptr<const typename BasicJsonType::boolean_t *>();
}
template <typename BasicJsonType>
void from_json(const BasicJsonType &j, typename BasicJsonType::string_t &s)
{
    if (not j.is_string())
    {
        JSON_THROW(type_error::create(302, "type must be string, but is " +
                                               j.type_name()));
    }
    s = *j.template get_ptr<const typename BasicJsonType::string_t *>();
}
template <typename BasicJsonType>
void from_json(const BasicJsonType &j,
               typename BasicJsonType::number_float_t &val)
{
    get_arithmetic_value(j, val);
}
template <typename BasicJsonType>
void from_json(const BasicJsonType &j,
               typename BasicJsonType::number_unsigned_t &val)
{
    get_arithmetic_value(j, val);
}
template <typename BasicJsonType>
void from_json(const BasicJsonType &j,
               typename BasicJsonType::number_integer_t &val)
{
    get_arithmetic_value(j, val);
}
template <typename BasicJsonType, typename EnumType,
          enable_if_t<std::is_enum<EnumType>::value, int> = 0>
void from_json(const BasicJsonType &j, EnumType &e)
{
    typename std::underlying_type<EnumType>::type val;
    get_arithmetic_value(j, val);
    e = static_cast<EnumType>(val);
}
template <typename BasicJsonType>
void from_json(const BasicJsonType &j, typename BasicJsonType::array_t &arr)
{
    if (not j.is_array())
    {
        JSON_THROW(type_error::create(302, "type must be array, but is " +
                                               j.type_name()));
    }
    arr = *j.template get_ptr<const typename BasicJsonType::array_t *>();
}
// forward_list doesn't have an insert method
template <typename BasicJsonType, typename T, typename Allocator,
          enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType &j, std::forward_list<T, Allocator> &l)
{
    if (not j.is_array())
    {
        JSON_THROW(type_error::create(302, "type must be array, but is " +
                                               j.type_name()));
    }
    for (auto it = j.rbegin(), end = j.rend(); it != end; ++it)
    {
        l.push_front(it->template get<T>());
    }
}
template <typename BasicJsonType, typename CompatibleArrayType>
void from_json_array_impl(const BasicJsonType &j, CompatibleArrayType &arr,
                          priority_tag<0>)
{
    using std::begin;
    using std::end;

    std::transform(
        j.begin(), j.end(), std::inserter(arr, end(arr)),
        [](const BasicJsonType &i) {
            // get<BasicJsonType>() returns *this, this won't call a from_json
            // method when value_type is BasicJsonType
            return i.template get<typename CompatibleArrayType::value_type>();
        });
}
template <typename BasicJsonType, typename CompatibleArrayType>
auto from_json_array_impl(const BasicJsonType &j, CompatibleArrayType &arr,
                          priority_tag<1>)
    -> decltype(
        arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
        void())
{
    using std::begin;
    using std::end;

    arr.reserve(j.size());
    std::transform(
        j.begin(), j.end(), std::inserter(arr, end(arr)),
        [](const BasicJsonType &i) {
            // get<BasicJsonType>() returns *this, this won't call a from_json
            // method when value_type is BasicJsonType
            return i.template get<typename CompatibleArrayType::value_type>();
        });
}
template <
    typename BasicJsonType, typename CompatibleArrayType,
    enable_if_t<
        is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and
            std::is_convertible<BasicJsonType, typename CompatibleArrayType::
                                                   value_type>::value and
            not std::is_same<typename BasicJsonType::array_t,
                             CompatibleArrayType>::value,
        int> = 0>
void from_json(const BasicJsonType &j, CompatibleArrayType &arr)
{
    if (not j.is_array())
    {
        JSON_THROW(type_error::create(302, "type must be array, but is " +
                                               j.type_name()));
    }
    from_json_array_impl(j, arr, priority_tag<1>{});
}
template <typename BasicJsonType, typename CompatibleObjectType,
          enable_if_t<is_compatible_object_type<BasicJsonType,
                                                CompatibleObjectType>::value,
                      int> = 0>
void from_json(const BasicJsonType &j, CompatibleObjectType &obj)
{
    if (not j.is_object())
    {
        JSON_THROW(type_error::create(302, "type must be object, but is " +
                                               j.type_name()));
    }
    auto inner_object =
        j.template get_ptr<const typename BasicJsonType::object_t *>();
    using std::begin;
    using std::end;
    // we could avoid the assignment, but this might require a for loop, which
    // might be less efficient than the container constructor for some
    // containers (would it?)
    obj = CompatibleObjectType(begin(*inner_object), end(*inner_object));
}
// overload for arithmetic types, not chosen for basic_json template arguments
// (BooleanType, etc..); note: Is it really necessary to provide explicit
// overloads for boolean_t etc. in case of a custom BooleanType which is not
// an arithmetic type?
template <
    typename BasicJsonType, typename ArithmeticType,
    enable_if_t<
        std::is_arithmetic<ArithmeticType>::value and
            not std::is_same<ArithmeticType, typename BasicJsonType::
                                                 number_unsigned_t>::value and
            not std::is_same<ArithmeticType, typename BasicJsonType::
                                                 number_integer_t>::value and
            not std::is_same<ArithmeticType,
                             typename BasicJsonType::number_float_t>::value and
            not std::is_same<ArithmeticType,
                             typename BasicJsonType::boolean_t>::value,
        int> = 0>
void from_json(const BasicJsonType &j, ArithmeticType &val)
{
    switch (static_cast<value_t>(j))
    {
    case value_t::number_unsigned:
    {
        val = static_cast<ArithmeticType>(
            *j.template get_ptr<
                const typename BasicJsonType::number_unsigned_t *>());
        break;
    }
    case value_t::number_integer:
    {
        val = static_cast<ArithmeticType>(
            *j.template get_ptr<
                const typename BasicJsonType::number_integer_t *>());
        break;
    }
    case value_t::number_float:
    {
        val = static_cast<ArithmeticType>(
            *j.template get_ptr<
                const typename BasicJsonType::number_float_t *>());
        break;
    }
    case value_t::boolean:
    {
        val = static_cast<ArithmeticType>(
            *j.template get_ptr<const typename BasicJsonType::boolean_t *>());
        break;
    }
    default:
    {
        JSON_THROW(type_error::create(302, "type must be number, but is " +
                                               j.type_name()));
    }
    }
}
struct to_json_fn
{
private:
    template <typename BasicJsonType, typename T>
    auto call(BasicJsonType &j, T &&val, priority_tag<1>) const
        noexcept(noexcept(to_json(j, std::forward<T>(val))))
            -> decltype(to_json(j, std::forward<T>(val)), void())
    {
        return to_json(j, std::forward<T>(val));
    }
    template <typename BasicJsonType, typename T>
    void call(BasicJsonType &, T &&, priority_tag<0>) const noexcept
    {
        static_assert(sizeof(BasicJsonType) == 0,
                      "could not find to_json() method in T's namespace");
    }
public:
    template <typename BasicJsonType, typename T>
    void operator()(BasicJsonType &j, T &&val) const
        noexcept(noexcept(std::declval<to_json_fn>().call(j,
                                                          std::forward<T>(val),
                                                          priority_tag<1>{})))
    {
        return call(j, std::forward<T>(val), priority_tag<1>{});
    }
};
struct from_json_fn
{
private:
    template <typename BasicJsonType, typename T>
    auto call(const BasicJsonType &j, T &val, priority_tag<1>) const
        noexcept(noexcept(from_json(j, val)))
            -> decltype(from_json(j, val), void())
    {
        return from_json(j, val);
    }
    template <typename BasicJsonType, typename T>
    void call(const BasicJsonType &, T &, priority_tag<0>) const noexcept
    {
        static_assert(sizeof(BasicJsonType) == 0,
                      "could not find from_json() method in T's namespace");
    }
public:
    template <typename BasicJsonType, typename T>
    void operator()(const BasicJsonType &j, T &val) const
        noexcept(noexcept(std::declval<from_json_fn>().call(j, val,
                                                            priority_tag<1>{})))
    {
        return call(j, val, priority_tag<1>{});
    }
};
// taken from ranges-v3
template <typename T>
struct static_const
{
    static constexpr T value{};
};
template <typename T>
constexpr T static_const<T>::value;
} // namespace detail
/// namespace to hold default `to_json` / `from_json` functions
namespace
{
constexpr const auto &to_json = detail::static_const<detail::to_json_fn>::value;
constexpr const auto &from_json =
    detail::static_const<detail::from_json_fn>::value;
}
/*!
@brief default JSONSerializer template argument
This serializer ignores the template arguments and uses ADL
([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl))
for serialization.
*/
template <typename = void, typename = void>
struct adl_serializer
{
    /*!
    @brief convert a JSON value to any value type
    This function is usually called by the `get()` function of the
    @ref basic_json class (either explicit or via conversion operators).
    @param[in] j         JSON value to read from
    @param[in,out] val  value to write to
    template <typename BasicJsonType, typename ValueType>
    static void from_json(BasicJsonType &&j, ValueType &val) noexcept(
        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
    {
        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
    }
    @brief convert any value type to a JSON value
    This function is usually called by the constructors of the @ref basic_json
    class.
    @param[in,out] j  JSON value to write to
    @param[in] val     value to read from
    */
    template <typename BasicJsonType, typename ValueType>
    static void to_json(BasicJsonType &j, ValueType &&val) noexcept(
        noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
    {
        ::nlohmann::to_json(j, std::forward<ValueType>(val));
    }
};
/*!
@brief a class to store JSON values
@tparam ObjectType type for JSON objects (`std::map` by default; will be used
in @ref object_t)
@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used
in @ref array_t)
@tparam StringType type for JSON strings and object keys (`std::string` by
default; will be used in @ref string_t)
@tparam BooleanType type for JSON booleans (`bool` by default; will be used
in @ref boolean_t)
@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by
default; will be used in @ref number_integer_t)
@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c
`uint64_t` by default; will be used in @ref number_unsigned_t)
@tparam NumberFloatType type for JSON floating-point numbers (`double` by
default; will be used in @ref number_float_t)
@tparam AllocatorType type of the allocator to use (`std::allocator` by
default)
@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`
and `from_json()` (@ref adl_serializer by default)
@requirement The class satisfies the following concept requirements:
- Basic
 -
[DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible):
   JSON values can be default constructed. The result will be a JSON null
   value.
 -
[MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible):
   A JSON value can be constructed from an rvalue argument.
 -
[CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible):
   A JSON value can be copy-constructed from an lvalue expression.
 - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable):
   A JSON value van be assigned from an rvalue argument.
 - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable):
   A JSON value can be copy-assigned from an lvalue expression.
 - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible):
   JSON values can be destructed.
- Layout
 -
[StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType):
   JSON values have
   [standard
layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout):
   All non-static data members are private and standard layout types, the
   class has no virtual functions or (virtual) base classes.
- Library-wide
 -
[EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable):
   JSON values can be compared with `==`, see @ref
   operator==(const_reference,const_reference).
 -
[LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable):
   JSON values can be compared with `<`, see @ref
   operator<(const_reference,const_reference).
 - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable):
   Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of
   other compatible types, using unqualified function call @ref swap().
 - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer):
   JSON values can be compared against `std::nullptr_t` objects which are used
   to model the `null` value.
- Container
 - [Container](http://en.cppreference.com/w/cpp/concept/Container):
   JSON values can be used like STL containers and provide iterator access.
 -
[ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer);
   JSON values can be used like STL containers and provide reverse iterator
   access.
@invariant The member variables @a m_value and @a m_type have the following
relationship:
- If `m_type == value_t::object`, then `m_value.object != nullptr`.
- If `m_type == value_t::array`, then `m_value.array != nullptr`.
- If `m_type == value_t::string`, then `m_value.string != nullptr`.
The invariants are checked by member function assert_invariant().
@internal
@note ObjectType trick from http://stackoverflow.com/a/9860911
@endinternal
@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange
Format](http://rfc7159.net/rfc7159)
@since version 1.0.0
@nosubgrouping
*/
template <template <typename U, typename V, typename... Args> class ObjectType =
              std::map,
          template <typename U, typename... Args> class ArrayType = std::vector,
          class StringType = std::string, class BooleanType = bool,
          class NumberIntegerType = std::int64_t,
          class NumberUnsignedType = std::uint64_t,
          class NumberFloatType = double,
          template <typename U> class AllocatorType = std::allocator,
          template <typename T, typename SFINAE = void> class JSONSerializer =
              adl_serializer>
class basic_json
{
private:
    template <detail::value_t>
    friend struct detail::external_constructor;
    /// workaround type for MSVC
    using basic_json_t =
        basic_json<ObjectType, ArrayType, StringType, BooleanType,
                   NumberIntegerType, NumberUnsignedType, NumberFloatType,
                   AllocatorType, JSONSerializer>;
public:
    using value_t = detail::value_t;
    // forward declarations
    template <typename U>
    class iter_impl;
    template <typename Base>
    class json_reverse_iterator;
    class json_pointer;
    template <typename T, typename SFINAE>
    using json_serializer = JSONSerializer<T, SFINAE>;
    ////////////////
    // exceptions //
    ////////////////
    /// @name exceptions
    /// Classes to implement user-defined exceptions.
    /// @{
    /// @copydoc detail::exception
    using exception = detail::exception;
    /// @copydoc detail::parse_error
    using parse_error = detail::parse_error;
    /// @copydoc detail::invalid_iterator
    using invalid_iterator = detail::invalid_iterator;
    /// @copydoc detail::type_error
    using type_error = detail::type_error;
    /// @copydoc detail::out_of_range
    using out_of_range = detail::out_of_range;
    /// @copydoc detail::other_error
    using other_error = detail::other_error;
    /////////////////////
    // container types //
    /////////////////////
    /// @name container types
    /// The canonic container types to use @ref basic_json like any other STL
    /// container.
    /// @{
    /// the type of elements in a basic_json container
    using value_type = basic_json;
    /// the type of an element reference
    using reference = value_type &;
    /// the type of an element const reference
    using const_reference = const value_type &;
    /// a type to represent differences between iterators
    using difference_type = std::ptrdiff_t;
    /// a type to represent container sizes
    using size_type = std::size_t;
    /// the allocator type
    using allocator_type = AllocatorType<basic_json>;
    /// the type of an element pointer
    using pointer = typename std::allocator_traits<allocator_type>::pointer;
    /// the type of an element const pointer
    using const_pointer =
        typename std::allocator_traits<allocator_type>::const_pointer;
    /// an iterator for a basic_json container
    using iterator = iter_impl<basic_json>;
    /// a const iterator for a basic_json container
    using const_iterator = iter_impl<const basic_json>;
    /// a reverse iterator for a basic_json container
    using reverse_iterator =
        json_reverse_iterator<typename basic_json::iterator>;
    /// a const reverse iterator for a basic_json container
    using const_reverse_iterator =
        json_reverse_iterator<typename basic_json::const_iterator>;
    @brief returns the allocator associated with the container
    */
    static allocator_type get_allocator() { return allocator_type(); }
    /*!
    @brief returns version information on the library
    This function returns a JSON object with information about the library,
    including the version number and information on the platform and compiler.

    @return JSON object holding version information
    key         | description
    ----------- | ---------------
    `compiler`  | Information on the used compiler. It is an object with the
    following keys: `c++` (the used C++ standard), `family` (the compiler
    family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`,
    `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).
    `copyright` | The copyright line for the library as string.
    `name`      | The name of the library as string.
    `platform`  | The used platform as string. Possible values are `win32`,
    `linux`, `apple`, `unix`, and `unknown`.
    `url`       | The URL of the project as string.
    `version`   | The version of the library. It is an object with the following
    keys: `major`, `minor`, and `patch` as defined by [Semantic
    Versioning](http://semver.org), and `string` (the version string).

    @liveexample{The following code shows an example output of the `meta()`
    function.,meta}
    @complexity Constant.
    @since 2.1.0
    static basic_json meta()
        basic_json result;
        result["copyright"] = "(C) 2013-2017 Niels Lohmann";
        result["name"] = "JSON for Modern C++";
        result["url"] = "https://github.com/nlohmann/json";
        result["version"] = {
            {"string", "2.1.1"}, {"major", 2}, {"minor", 1}, {"patch", 1}};
#ifdef _WIN32
        result["platform"] = "win32";
#elif defined __linux__
        result["platform"] = "linux";
#elif defined __APPLE__
        result["platform"] = "apple";
#elif defined __unix__
        result["platform"] = "unix";
#else
        result["platform"] = "unknown";
#endif
#if defined(__clang__)
        result["compiler"] = {{"family", "clang"},
                              {"version", __clang_version__}};
#elif defined(__ICC) || defined(__INTEL_COMPILER)
        result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
#elif defined(__GNUC__) || defined(__GNUG__)
        result["compiler"] = {
            {"family", "gcc"},
            {"version", std::to_string(__GNUC__) + "." +
                            std::to_string(__GNUC_MINOR__) + "." +
                            std::to_string(__GNUC_PATCHLEVEL__)}};
#elif defined(__HP_cc) || defined(__HP_aCC)
        result["compiler"] = "hp"
#elif defined(__IBMCPP__)
        result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
#elif defined(_MSC_VER)
        result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
#elif defined(__PGI)
        result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
#elif defined(__SUNPRO_CC)
        result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
#else
        result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
#endif
#ifdef __cplusplus
        result["compiler"]["c++"] = std::to_string(__cplusplus);
#else
        result["compiler"]["c++"] = "unknown";
#endif
        return result;
    }
    ///////////////////////////
    // JSON value data types //
    ///////////////////////////
    /// @name JSON value data types
    /// The data types to store a JSON value. These types are derived from
    /// the template arguments passed to class @ref basic_json.
    /// @{
    /*!
    @brief a type for an object
    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows:
    > An object is an unordered collection of zero or more name/value pairs,
    > where a name is a string and a value is a string, number, boolean, null,
    > object, or array.
    To store objects in C++, a type is defined by the template parameters
    described below.
    @tparam ObjectType  the container to store objects (e.g., `std::map` or
    `std::unordered_map`)
    @tparam StringType the type of the keys or names (e.g., `std::string`).
    The comparison function `std::less<StringType>` is used to order elements
    inside the container.
    @tparam AllocatorType the allocator to use for objects (e.g.,
    `std::allocator`)
    #### Default type
    With the default values for @a ObjectType (`std::map`), @a StringType
    (`std::string`), and @a AllocatorType (`std::allocator`), the default
    value for @a object_t is:
    @code {.cpp}
    std::map<
      std::string, // key_type
      basic_json, // value_type
      std::less<std::string>, // key_compare
      std::allocator<std::pair<const std::string, basic_json>> // allocator_type
    >
    @endcode
    #### Behavior
    The choice of @a object_t influences the behavior of the JSON class. With
    the default type, objects have the following behavior:
    - When all names are unique, objects will be interoperable in the sense
      that all software implementations receiving that object will agree on
      the name-value mappings.
    - When the names within an object are not unique, later stored name/value
      pairs overwrite previously stored name/value pairs, leaving the used
      names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will
      be treated as equal and both stored as `{"key": 1}`.
    - Internally, name/value pairs are stored in lexicographical order of the
      names. Objects will also be serialized (see @ref dump) in this order.
      For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored
      and serialized as `{"a": 2, "b": 1}`.
    - When comparing objects, the order of the name/value pairs is irrelevant.
      This makes objects interoperable in the sense that they will not be
      affected by these differences. For instance, `{"b": 1, "a": 2}` and
      `{"a": 2, "b": 1}` will be treated as equal.
    #### Limits
    [RFC 7159](http://rfc7159.net/rfc7159) specifies:
    > An implementation may set limits on the maximum depth of nesting.
    In this class, the object's limit of nesting is not constraint explicitly.
    However, a maximum depth of nesting may be introduced by the compiler or
    runtime environment. A theoretical limit can be queried by calling the
    @ref max_size function of a JSON object.
    #### Storage
    Objects are stored as pointers in a @ref basic_json type. That is, for any
    access to object values, a pointer of type `object_t*` must be
    dereferenced.
    @sa @ref array_t -- type for an array value

    @note The order name/value pairs are added to the object is *not*
    preserved by the library. Therefore, iterating an object may return
    name/value pairs in a different order than they were originally stored. In
    fact, keys will be traversed in alphabetical order as `std::map` with
    `std::less` is used by default. Please note this behavior conforms to [RFC
    7159](http://rfc7159.net/rfc7159), because any order implements the
    specified "unordered" nature of JSON objects.
    using object_t =
        ObjectType<StringType, basic_json, std::less<StringType>,
                   AllocatorType<std::pair<const StringType, basic_json>>>;

    /*!
    @brief a type for an array
    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows:
    > An array is an ordered sequence of zero or more values.
    To store objects in C++, a type is defined by the template parameters
    explained below.
    @tparam ArrayType  container type to store arrays (e.g., `std::vector` or
    `std::list`)
    @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)
    #### Default type
    With the default values for @a ArrayType (`std::vector`) and @a
    AllocatorType (`std::allocator`), the default value for @a array_t is:
    @code {.cpp}
    std::vector<
      basic_json, // value_type
      std::allocator<basic_json> // allocator_type
    >
    @endcode
    #### Limits
    [RFC 7159](http://rfc7159.net/rfc7159) specifies:
    > An implementation may set limits on the maximum depth of nesting.
    In this class, the array's limit of nesting is not constraint explicitly.
    However, a maximum depth of nesting may be introduced by the compiler or
    runtime environment. A theoretical limit can be queried by calling the
    @ref max_size function of a JSON array.
    #### Storage
    Arrays are stored as pointers in a @ref basic_json type. That is, for any
    access to array values, a pointer of type `array_t*` must be dereferenced.
    @sa @ref object_t -- type for an object value
    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
    /*!
    @brief a type for a string
    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows:
    > A string is a sequence of zero or more Unicode characters.
    To store objects in C++, a type is defined by the template parameter
    described below. Unicode values are split by the JSON class into
    byte-sized characters during deserialization.
    @tparam StringType  the container to store strings (e.g., `std::string`).
    Note this container is used for keys/names in objects, see @ref object_t.
    #### Default type
    With the default values for @a StringType (`std::string`), the default
    value for @a string_t is:
    @code {.cpp}
    std::string
    @endcode
    #### Encoding
    Strings are stored in UTF-8 encoding. Therefore, functions like
    `std::string::size()` or `std::string::length()` return the number of
    bytes in the string rather than the number of characters or glyphs.

    #### String comparison

    [RFC 7159](http://rfc7159.net/rfc7159) states:
    > Software implementations are typically required to test names of object
    > members for equality. Implementations that transform the textual
    > representation into sequences of Unicode code units and then perform the
    > comparison numerically, code unit by code unit, are interoperable in the
    > sense that implementations will agree in all cases on equality or
    > inequality of two strings. For example, implementations that compare
    > strings with escaped characters unconverted may incorrectly find that
    > `"a\\b"` and `"a\u005Cb"` are not equal.

    This implementation is interoperable as it does compare strings code unit
    by code unit.

    #### Storage
    String values are stored as pointers in a @ref basic_json type. That is,
    for any access to string values, a pointer of type `string_t*` must be
    dereferenced.
    using string_t = StringType;
    @brief a type for a boolean
    [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a
    type which differentiates the two literals `true` and `false`.
    To store objects in C++, a type is defined by the template parameter @a
    BooleanType which chooses the type to use.
    #### Default type
    With the default values for @a BooleanType (`bool`), the default value for
    @a boolean_t is:

    @code {.cpp}
    bool
    @endcode

    #### Storage

    Boolean values are stored directly inside a @ref basic_json type.
    using boolean_t = BooleanType;
    @brief a type for a number (integer)

    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
    > The representation of numbers is similar to that used in most
    > programming languages. A number is represented in base 10 using decimal
    > digits. It contains an integer component that may be prefixed with an
    > optional minus sign, which may be followed by a fraction part and/or an
    > exponent part. Leading zeros are not allowed. (...) Numeric values that
    > cannot be represented in the grammar below (such as Infinity and NaN)
    > are not permitted.

    This description includes both integer and floating-point numbers.
    However, C++ allows more precise storage if it is known whether the number
    is a signed integer, an unsigned integer or a floating-point number.
    Therefore, three different types, @ref number_integer_t, @ref
    number_unsigned_t and @ref number_float_t are used.
    To store integer numbers in C++, a type is defined by the template
    parameter @a NumberIntegerType which chooses the type to use.
    #### Default type
    With the default values for @a NumberIntegerType (`int64_t`), the default
    value for @a number_integer_t is:
    @code {.cpp}
    int64_t
    @endcode
    #### Default behavior
    - The restrictions about leading zeros is not enforced in C++. Instead,
      leading zeros in integer literals lead to an interpretation as octal
      number. Internally, the value will be stored as decimal number. For
      instance, the C++ integer literal `010` will be serialized to `8`.
      During deserialization, leading zeros yield an error.
    - Not-a-number (NaN) values will be serialized to `null`.
    #### Limits
    [RFC 7159](http://rfc7159.net/rfc7159) specifies:
    > An implementation may set limits on the range and precision of numbers.
    When the default type is used, the maximal integer number that can be
    stored is `9223372036854775807` (INT64_MAX) and the minimal integer number
    that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers
    that are out of range will yield over/underflow when used in a
    constructor. During deserialization, too large or small integer numbers
    will be automatically be stored as @ref number_unsigned_t or @ref
    number_float_t.
    [RFC 7159](http://rfc7159.net/rfc7159) further states:
    > Note that when such software is used, numbers that are integers and are
    > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense
    > that implementations will agree exactly on their numeric values.
    As this range is a subrange of the exactly supported range [INT64_MIN,
    INT64_MAX], this class's integer type is interoperable.
    #### Storage
    Integer number values are stored directly inside a @ref basic_json type.
    @sa @ref number_float_t -- type for number values (floating-point)
    @sa @ref number_unsigned_t -- type for number values (unsigned integer)
    using number_integer_t = NumberIntegerType;
    @brief a type for a number (unsigned)

    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
    > The representation of numbers is similar to that used in most
    > programming languages. A number is represented in base 10 using decimal
    > digits. It contains an integer component that may be prefixed with an
    > optional minus sign, which may be followed by a fraction part and/or an
    > exponent part. Leading zeros are not allowed. (...) Numeric values that
    > cannot be represented in the grammar below (such as Infinity and NaN)
    > are not permitted.
    This description includes both integer and floating-point numbers.
    However, C++ allows more precise storage if it is known whether the number
    is a signed integer, an unsigned integer or a floating-point number.
    Therefore, three different types, @ref number_integer_t, @ref
    number_unsigned_t and @ref number_float_t are used.
    To store unsigned integer numbers in C++, a type is defined by the
    template parameter @a NumberUnsignedType which chooses the type to use.
    #### Default type
    With the default values for @a NumberUnsignedType (`uint64_t`), the
    default value for @a number_unsigned_t is:
    @code {.cpp}
    uint64_t
    @endcode
    #### Default behavior
    - The restrictions about leading zeros is not enforced in C++. Instead,
      leading zeros in integer literals lead to an interpretation as octal
      number. Internally, the value will be stored as decimal number. For
      instance, the C++ integer literal `010` will be serialized to `8`.
      During deserialization, leading zeros yield an error.
    - Not-a-number (NaN) values will be serialized to `null`.
    #### Limits
    [RFC 7159](http://rfc7159.net/rfc7159) specifies:
    > An implementation may set limits on the range and precision of numbers.
    When the default type is used, the maximal integer number that can be
    stored is `18446744073709551615` (UINT64_MAX) and the minimal integer
    number that can be stored is `0`. Integer numbers that are out of range
    will yield over/underflow when used in a constructor. During
    deserialization, too large or small integer numbers will be automatically
    be stored as @ref number_integer_t or @ref number_float_t.
    [RFC 7159](http://rfc7159.net/rfc7159) further states: