????R0
A Minimal JSON Support Library for C++

Draft Proposal,

This version:
https://github.com/YexuanXiao/basic_json/blob/master/proposal.bs
Authors:
Audience:
LEWG
Project:
ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21

Abstract

This paper proposes a minimal JSON support library for C++, which provides a simple and efficient way to represent and manipulate JSON data. The library consists of four main components: a basic_json class that represents a JSON value, a basic_json_node class that provide a type-erased JSON storage, and two slice classes that provides access and modification operations to the JSON class. The library aims to be compatible with the existing C++ standard library, and provides strong extension support.

1. Motivation

JSON is an internet standard, widely used for data transmission and storage, but the C++ standard library lacks support for JSON, which forces C++ users to choose among third-party libraries.

As the C++ standard evolves, C++ becomes more and more suitable for network programming, and C++'s high performance makes C++ equally suitable for processing large amounts of data stored by JSON, so adding JSON to the standard library is beneficial and harmless to C++.

There are many third-party libraries that provide JSON support for C++, but they have some drawbacks, such as:

Therefore, this proposal aims to provide a minimal JSON support library for C++, which can address these issues, and offer the following benefits:

2. Proposal

I propose to add a <json> header and the following classes (templates): nulljson_t, basic_json_node, basic_json, basic_const_json_slice, basic_json_slice. The json_errc enumeration and the json_error exception class are used to report errors.

struct nulljson_t;

inline constexpr nulljson_t nulljson{};

template <typename Number,
  typename Integer, typename UInteger, typename Allocator>
class basic_json_node;

template <typename Node, typename String,
  typename Array, typename Object,
  bool HasInteger, bool HasUInteger>
class basic_json;

template <typename Node, typename String,
  typename Array, typename Object,
  bool HasInteger, bool HasUInteger>
class basic_const_json_slice;

template <typename Node, typename String,
  typename Array, typename Object,
  bool HasInteger, bool HasUInteger>
class basic_json_slice;

enum class errc;

class json_error;

3. Design

Since JSON has a self-referential structure ([RFC8259]), type erasure must be used.

json.org’s JSON structure diagram: object json.org’s JSON structure diagram: value

3.1. nulljson/nulljson_t

nulljson is a type similar to nullopt, used to indicate that the value of JSON is null.

nulljson_t is the type of nulljson. It is a trivial, empty class with a explicit default constructor, like all other construction tag types in the standard library.

3.2. basic_json_node

basic_json_node has four template parameters: Number, Integer, UInteger, and Allocator. Users can use these template parameters to customize their preferred types and allocators.

For example, some users may prefer to use fixed-length integer types, some users may prefer to use integer types written in by C++ keywords, and the same for floating-point types.

basic_json_node holds an allocator and a tagged union that stores one of a boolean, number (floating-point), signed integer, unsigned integer, string, array (vector), or object (map), where the tag is represented by an enumeration value.

basic_json_node is a substitute for basic_json, providing storage space for basic_json in any situation where circular dependencies may occur.

basic_json_node is conceptually similar to a raw pointer. It does not always own memory, but can transfer memory through it.

Trivial copyability of basic_json_node depends on Allocator. If Allocator is trivially copyable, then so is basic_json_node, and basic_json arrays will get faster copy speed.

3.3. basic_json

basic_json is a semiregular type that represents ownership of a JSON structure. It can be implemented as storing a basic_json_node as its only non-static data member, which makes basic_json and basic_json_node have the same size. Its destructor is responsible for destructing all nodes and deallocate all dynamic storage held by the object. The copy constructor and copy assignment operator copy the JSON. The swap operation is provided as both a hidden friend function and a non-static member function.

The reason why the allocator is a template parameter of basic_json_node rather than basic_json is that basic_json must have the same size as basic_json_node, so std::byte is usually used to instantiate the allocator (void type can be used after LWG issue [3917] is resolved), and then rebind is used to allocate storage. Once a specialization of basic_json_node is available, basic_json can be instantiated. basic_json has six template parameters: Node, String, Array, Object, HasInteger, HasUInteger.

Node must be a specialization of basic_json_node, and since basic_json_node provides type aliases to obtain the template arguments, basic_json can extract these type aliases, rewrite the specialization of basic_json_node, and compare it with Node to ensure this.

For arithmetic types, they are directly stored in the union, and since map and array store basic_json_node, pointers are needed to break the circular dependency, and since the two types are not determined in basic_json_node, these pointers are actually of type void*. Conceptually, basic_json is a hybrid of container adapters and containers.

Although string type does not have circular dependency problem, void* is also used to save the space. The relationship between basic_json and basic_json_node is shown in the following figure:

relationship diagram between node, json and slice

Therefore, the Allocator template parameter of basic_json_node is not used directly, but always rebound to the allocators of string, array, and object.

Most of the member functions of basic_json are constructors, which make C++ values easily convertible to a JSON type.

In addition, basic_json also has a &&-qualified to node_type conversion function, which can transfer memory from basic_json to basic_json_node, just like unique_ptr::release.

The most special point of my proposal is to expose basic_json_node, which allows users to implement their own serializer and deserializer in a non-intrusive way: if a basic_json object that stores a boolean or arithmetic type value is needed, then construct it directly through the constructor, if a basic_json object that stores an array or object type is needed, then users can construct array and object themselves, such as std::vector<basic_json_node<>> a and std::map<std::string, basic_json_node> m, then construct basic_json objects through the constructors of boolean or arithmetic types, and then insert them into the container, finally, move a or m to the constructor of basic_json, and get a basic_json object that represents an array or map.

3.4. basic_json_slice/basic_const_json_slice

basic_json_slice and basic_const_json_slice are similar to iterators of containers. basic_const_json_slice is constructible from a possibly const basic_json or basic_json_node and holds a pointer to that basic_json_node object. All non-static functions of basic_const_json_slice are const-qualified, and return either a value or a reference to a const object.

basic_json_slice has all the member functions that basic_const_json_slice has, and can be converted to basic_const_json_slice. In addition, basic_const_json_slice also has modifiers (overload of assignment operators), which can modify the value without changing the type of the value stored in the node.

basic_json_slice behaves like propagate_const in the Library Fundamentals TS ([LFTSv3]). Operations on a const basic_json_slice value also access the referred to node in a const manner, like a basic_const_json_slice. Only operations on a non-const basic_json_slice possibly modifies the node.

basic_json_slice and basic_const_json_slice are trivially copyable, so copying a slice has low overhead. No operation on a slice produces a copy of the basic_json object. For subscript operations, a new basic_json_slice or basic_const_json_slice is returned.

A default-constructed slice holds a null pointer, and thus all operations that need to queries or modifies the node results in undefined behavior on such a slice. The default-constructibility of slices is mainly used for default arguments.

3.5. Summary

This design makes the basic_json template independent of the specific vector type, map type, string type. One can use std::map, std::flat_map, or std::unordered_map if desires. Additionally, the string type as the Key of the Object and the String type as the Value can be different, which makes KeyString can be implemented with a dictionary that records all possible values.

This design does not care whether the string type has a charactor traits or allocator type. In extreme cases, this design allows both strings to be std::string_view, such as when mapping the JSON byte stream to memory, each std::string_view references a part of the memory. This makes it possible to not copy any strings (but still need to use dynamic memory to store maps and arrays).

4. Implementation experience

I have provided a simple implementation on Github, the source code of the document is available in the same repository.

Currently, stateful allocators are not properly supported, because I do not have much experience with allocators. I have not provided serialization and deserialization functions, because the C++ standard library currently does not have a good IO API, and different users may have their own serialization and deserialization needs, so this is not part of the proposal.

The design is feasible and stable, but I need some feedback to appropriately increase the usability of the library.

5. Use cases

#include "basic_json.hpp"
#include <string_view>
#include <cassert>
int main()
{
  using json = bizwen::json;
  using node = bizwen::json_node;
  using slice = bizwen::json_slice;
  using const_slice = bizwen::const_json_slice;
  auto null = bizwen::nulljson;
  using namespace std::literals;
  // A json object represents a json value. The default constructed json object does not hold any value, and its state is "undefined".
  json j01{};
  // Construct a json value with status "number" and value `1`.
  json j02{ 1. };
  // Construct a json value with status "true_value" and value `true`.
  json j03{ true };
  // Construct a json value with status "uinteger" and value `1`. This method is used to accurately store integers with more than 52 bits.
  json j04{ 1ull };
  // Construct a json value with status "string" and value `"abcdef"`.
  json j05{ "abcdef" };
  // Construct json with nullptr is forbidden because json does not have a pointer type and nullptr does not represent the empty string.
  // json j05{ nullptr };
  // Construct a json value with status "null" and value `nulljson`.
  json j06{ null };
  // Since initializer_list returns a reference to a const object, this method is inefficient.
  // json j07{ json::array_type{ json{0}, json{1} } };
  // json j08{ json::object_type{ { "key0"s, json{ 0 } }, { "key1"s, json{ 1 } } } };
  // Use the helper class templates json::array and json::object for easy and efficient construction of json arrays and json objects.
  // Constructs a json value with the state "array", containing two ordered values 0 and 1.
  json j07{ json::array{ 0, 1 } };
  // Construct a json value with state "object" such that `s08["key0"]==0` and `s08["key1"]==1` is true.
  json j08{ json::object{ "key0"s, 0, "key1"s, 1 } };
  // Copy a json object copies its stored state and values.
  auto j09{ j08 };

  // slice is an accessor and modifier of json values, and the default constructed slice is not associated with any json.
  slice s01{};
  // Use empty() to test if slice is associated with a json.
  auto is_empty{ s01.empty() };
  assert(is_empty); // does nothing
  slice s07{ j07 }; // s07 is associated to j07
  auto is_array{ s07.array() };
  assert(is_array); // does nothing
  // Convert a json value (s01) with state "undefined" to "integer" and set the value to `1`.
  s01 = 1;
  slice s02{ j02 }; // s02 is associated to j02
  // Change the value of j02 to 2.f
  s02 = 2.f;
  // Sets the state of j02 to "undefined" and destroys the previous value.
  s02.reset();
  long long iv{ s07[0] };

  // Iterate j07
  for (auto i : s07.as_array())
  {
    assert(i.integer()); // does nothing
  }
  // Append a value to j07
  // Due to CWG1996, list initialization cannot be used here.
  json::array_type& arr( s07 );
  arr.push_back(json{ 2 });
  slice s08{ j08 };
  // Iterate j08
  for (auto const& [k, v] : s08.as_object())
  {
    assert(v.integer()); // does nothing
  }
  // Insert k-v pairs into j08
  s08["key3"] = 2;
  // Copying a slice is trivial
  auto s08_1{ s08 };

  // const_slice is similar to slice, but has only observers and never modifies the associated json.
  const_slice c01{ s01 };
  // Unlike slice, if the key does not exist in the object then a json_error exception is thrown.
  try
  {
    c01["keyn"];
  }
  catch (bizwen::json_error const& je)
  {
    assert(je.code() == bizwen::json_errc::key_not_found); // does nothing
  }
}

6. Library wording

6.1. JSON processing

[Drafting note: Add this subclause to [utilities]. -- end drafting note]

6.1.1. Header <json> synopsis

namespace std {
  struct nulljson_t {
    explicit nulljson_t() = default;
  };

  inline constexpr nulljson_t nulljson;

  template<class Number = double, class Integer = long long, class UInteger = unsigned long long,
           class Allocator = allocator<byte>>
    class basic_json_node;

  template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
           class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
    class basic_json;

  template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
           class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
    class basic_json_slice;

  template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
           class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
    class basic_const_json_slice;

  using json             = basic_json<>;
  using json_node        = basic_json_node<>;
  using json_slice       = basic_json_slice<>;
  using const_json_slice = basic_const_json_slice<>;

  enum class json_errc : <i>unspecified</i>;

  class json_error;

  namespace pmr {
    template<class Number = double, class Integer = long long,
             class UInteger = unsigned long long>
    using basic_json_node = std::basic_json_node<Number, Integer, UInteger,
                                                 polymorphic_allocator<>>;

    template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
             class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
    using basic_json = std::basic_json<Node, String, Array, Object, HasInteger, HasUInteger>;

    template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
             class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
    using basic_json_slice = std::basic_json_slice<Node, String, Array, Object,
                                                   HasInteger, HasUInteger>;

    template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
             class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
    using basic_const_json_slice = std::basic_const_json_slice<Node, String, Array, Object,
                                                               HasInteger, HasUInteger>;

    using json             = basic_json<>;
    using json_node        = basic_json_node<>;
    using json_slice       = basic_json_slice<>;
    using const_json_slice = basic_const_json_slice<>;
  }
}

6.1.2. General

  1. A basic_json_node object is a JSON node. A valid node object is always in one of the following states: undefined, null, true, false, floating-point, signed integral, unsigned integral, string, array, and object.

  2. Some operations of basic_json and basic_json_slice dynamically allocate memory. This happens if and only if the basic_json_node held by basic_json or referenced by basic_json_slice is set to the string, array, or object state. In such a case, let

    • A be the allocator_type of the node type,

    • a be the allocator object of type A stored in the node,

    • X be the type for the state, which is String for the string state, Array for the array state, or Object for the object state.

    • AX be allocator_traits<A>::template rebind_alloc<X>,

    • ax be an lvalue of type AX that denotes an AX allocator object constructed from a.

    Such an aforementioned operation allocates the dynamic storage with allocator_traits<AX>::allocate(ax, 1), and then constructs the X object using allocator_traits<AX>::construct(ax, p, args...). If an exception is thrown when setting the state, the value of the node is unchanged.

  3. Likewise, the assignment operators and the destructor of basic_json destroy the object in the dynamic storage and then deallocate the storage when needed. Let

    • x be the X object to be destroyed,

    • pp be pointer_traits<allocator_traits<AX>::pointer>::pointer_to(x),

    x is destroyed using allocator_traits<AX>::destroy(ax, addressof(x)), and then the dynamic storage is deallocated with allocator_traits<AX>::deallocate(pp, 1).

  4. For the purpose of constraints of functions, the map type Object is considered transparently comparable if and only if

    • qualified-ids Object::key_compare and Object::key_compare::is_transparent are all valid and denote types, or

    • qualified-ids Object::key_equal, Object::key_equal::is_transparent, Object::hasher, and Object::hasher::is_transparent are all valid and denote types.

6.1.3. Class template basic_json_node

namespace std {
  template<class Number = double, class Integer = long long, class UInteger = unsigned long long,
           class Allocator = allocator>
    class basic_json_node {
    private:
      using void-ptr = allocator_traits::void_pointer;   // exposition only

    public:
      using number_type    = Number;
      using integer_type   = Integer;
      using uinteger_type  = UInteger;
      using allocator_type = Allocator;

      constexpr basic_json_node() noexcept(is_nothrow_default_constructible_v)
        requires default_initializable<Allocator>;
      constexpr explicit basic_json_node(const Allocator& a) noexcept;

      constexpr basic_json_node(const basic_json_node& other) noexcept;
      constexpr basic_json_node(basic_json_node&& other) noexcept;
      constexpr basic_json_node& operator=(const basic_json_node& other)
        noexcept(is_nothrow_copy_assignable_v<Allocator>);
      constexpr basic_json_node& operator=(basic_json_node&& other)
        noexcept(is_nothrow_move_assignable_v<Allocator>);
      constexpr ~basic_json_node() noexcept;
    };

  template<class Number, class Integer, class UInteger, class Allocator, class Allocator2>
    struct uses_allocator<basic_json_node<Number, Integer, UInteger, Allocator>, Allocator2> :
      false_type {};
}
  1. Mandates:

    • floating_point<Number>, signed_integral<Integer>, and unsigned_integral<UInteger> are all true.

    • Allocator meets the Cpp17Allocator requirements, no diagnostic is required for this.

  2. If the node is in the floating-point, signed integral, or unsigned integral state, an object of type number_type, integer_type, or uinteger_type stored within the node. If the node is in the string, array, or object state, an object of the type corresponding to the state is dynamically allocated, and a void-ptr to it is stored within the node. [Note: A single node type can refer to string, dynamic array, or map of different types. -- end note]

  3. [Note: The uses_allocator partial specialization indicates that basic_json_node should not be uses-allocator constructed. -- end note]

6.1.3.1. Construction, assignment, and destruction
constexpr basic_json_node() noexcept(is_nothrow_default_constructible_v<Allocator>)
  requires default_initializable<Allocator>;
  1. Effects: Initializes the node to the undefined state. Value-initializes the stored allocator.

constexpr explicit basic_json_node(const Allocator& a) noexcept;
  1. Effects: Initializes the node to the undefined state. Copy-constructs the stored allocator from a.

constexpr basic_json_node(const basic_json_node& other) noexcept;
constexpr basic_json_node(basic_json_node&& other) noexcept;
  1. Effects:

    • For the copy constructor, copy-constructs the stored allocator from that of other; for the move constructor, move construction is used instead.

    • Sets *this in the same state as other.

    • If other stores a numeric value, stores the same value in this.

    • Otherwise, if other stores a void-ptr to an allocated object, for the copy constructor, copy-constructs that pointer into *this; for the move constructor, move construction is used instead.

  2. Remarks:

    • The copy constructor is trivial if both is_trivially_copy_constructible_v<Allocator> and is_trivially_copy_constructible_v<void-ptr> are true.

    • The move constructor is trivial if both is_trivially_move_constructible_v<Allocator> and is_trivially_move_constructible_v<void-ptr> are true.

constexpr basic_json_node& operator=(const basic_json_node& other)
  noexcept(is_nothrow_copy_assignable_v<Allocator>);
constexpr basic_json_node& operator=(basic_json_node&& other)
  noexcept(is_nothrow_move_assignable_v<Allocator>);
  1. Constraints: For the move assignment operator, is_move_assignable_v<Allocator> is true.

  2. Effects:

    • If this == addressof(other), does nothing.

    • Otherwise, copy- or move-assign the stored allocator of *this from that of other, for copy or move assignment operator respectively. And then

      • if *this is in the same state as other and stores a numeric value or void-ptr, copy- or move-assign the stored numeric value or void-ptr in other to that of *this, for copy or move assignment operator respectively, otherwise,

      • destroys the stored a numeric value or void-ptr, if any, and then copy- or move-constructs the numeric value or void-ptr from that of other for copy or move assignment operator respectively, if any.

  3. Returns: *this.

  4. Remarks:

    • The copy assignment operator is deleted if is_copy_assignable_v<Allocator> is false.

    • The copy assignment operator is trivial if is_trivially_copy_assignable_v<Allocator>, is_trivially_destructible_v<void-ptr>, is_trivially_copy_constructible_v<void-ptr>, and is_trivially_copy_assignable_v<void-ptr> are all true.

    • The move assignment operator is trivial if is_trivially_move_assignable_v<Allocator>, is_trivially_destructible_v<void-ptr>, is_trivially_move_constructible_v<void-ptr>, and is_trivially_move_assignable_v<void-ptr> are all true.

constexpr ~basic_json_node() noexcept;
  1. Effects: Destroys the stored allocator, and the numeric value or the void-ptr, if any. [Note: No deallocation happens. -- end note]

  2. Remarks: The destructor is trivial if both is_trivially_destructible_v<Allocator> and is_trivially_destructible_v<void-ptr> are true.

6.1.4. Class template basic_json

namespace std {
  template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
           class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
    class basic_json {
    public:
      static constexpr bool has_integer  = HasInteger;
      static constexpr bool has_uinteger = HasUInteger;

      using slice_type       = basic_json_slice<Node, String, Array, Object,
                                                HasInteger, HasUInteger>;
      using const_slice_type = basic_const_json_slice<Node, String, Array, Object,
                                                      HasInteger, HasUInteger>;

      using number_type   = node_type::number_type;
      using integer_type  = node_type::integer_type;
      using uinteger_type = node_type::uinteger_type;

      using char_type       = string_type::value_type;
      using map_node_type   = object_type::value_type;
      using allocator_type  = node_type::allocator_type;
      using key_string_type = object_type::key_type;
      using key_char_type   = key_string_type::value_type;

      constexpr basic_json() requires default_initializable = default;
      constexpr explicit basic_json(const allocator_type& a) noexcept : node_(a) {}
      constexpr basic_json(const basic_json& rhs);
      constexpr basic_json(basic_json&& rhs) noexcept;
      constexpr basic_json& operator=(const basic_json& rhs);
      constexpr basic_json& operator=(basic_json&& rhs)
        noexcept(allocator_traits::propagate_on_container_move_assignment::value ||
                allocator_traits::is_always_equal::value);
      constexpr ~basic_json();
      constexpr void swap(basic_json& rhs)
        noexcept(allocator_traits::propagate_on_container_swap::value ||
                 allocator_traits::is_always_equal::value);
      friend constexpr void swap(basic_json& lhs, basic_json& rhs)
        noexcept(noexcept(lhs.swap(rhs)));

      constexpr basic_json(const basic_json& rhs, const allocator_type& a);
      constexpr basic_json(basic_json&& rhs, const allocator_type& a)
        noexcept(allocator_traits::is_always_equal::value);

      constexpr basic_json(nulljson_t, const allocator_type& a = allocator_type()) noexcept;
      template<class T>
        constexpr basic_json(T n, const allocator_type& a = allocator_type()) noexcept;
      constexpr explicit basic_json(string_type v, const allocator_type& a = allocator_type());
      constexpr basic_json(const char_type* first, const char_type* last,
                           const allocator_type& a = allocator_type());
      constexpr basic_json(const char_type* str, string_type::size_type count,
                           const allocator_type& a = allocator_type());
      constexpr explicit basic_json(const char_type* str,
                                    const allocator_type& a = allocator_type());
      template<class StrLike>
        constexpr basic_json(const StrLike& str, const allocator_type& a = allocator_type());
      constexpr explicit basic_json(array_type arr,
                                    const allocator_type& a = allocator_type());
      constexpr explicit basic_json(object_type obj,
                                    const allocator_type& a = allocator_type());
      constexpr explicit basic_json(node_type&& n) noexcept;
      constexpr explicit basic_json(node_type&& n, const allocator_type& a) noexcept;
      basic_json(nullptr_t, const allocator_type& = allocator_type()) = delete;

      constexpr allocator_type get_allocator() const noexcept;

      constexpr operator node_type() && noexcept;
      constexpr slice_type slice() noexcept;
      constexpr const_slice_type slice() const noexcept;

    private:
      node_type node_;   // exposition only

      // Helper Classes
      template<class... Ts>
      struct array;      // exposition only

      template<size_t N>
      struct object;     // exposition only

      template<class... Ts>
      object(Ts&&...) -> object<see below>;
    };
}
  1. Mandates:

    • Node is a well-formed specialization of basic_json_node.

    • qualified-id Array::value_type is valid and denotes the same type as Node.

    • qualified-id Object::mapped_type is valid and denotes the same type as Node.

6.1.4.1. Construction and swap
  1. Every constructor taking a const allocator_type& parameter constructs node_ from that parameter.

  2. Every constructor or assignment operator taking a basic_json&& parameter leaves the argument in a valid but unspecified state after construction or assignment. Except for the move constructor, each of these functions reuse the dynamic storage of the argument if and only if there is some dynamic storage allocated and the stored allocator in the node_ is equal to that of the argument.

  3. The copy constructors, move constructor, copy assignment operator, move assignment operator, and swap functions propagate the allocator value as specified in [container.alloc.reqmts].

constexpr basic_json(nulljson_t, const allocator_type& a = allocator_type()) noexcept;
  1. Effects: Sets node_ to the null state.

template<class T>
  constexpr basic_json(T n, const allocator_type& a = allocator_type()) noexcept;
  1. Constraints: is_arithmetic_v<T> is true.

  2. Effects:

    • If T is bool, sets node_ to the true or the false state if n is true or false, respectively.

    • Otherwise, if signed_integral<T> and has_integer are both true, sets node_ to the signed integral state and stores n.

    • Otherwise, if unsigned_integral<T> and has_uinteger are both true, sets node_ to the unsigned integral state and stores n.

    • Otherwise, sets node_ to the floating-point state and stores static_cast<number_type>(n).

constexpr explicit basic_json(string_type v, const allocator_type& a = allocator_type());
  1. Effects: Sets node_ to the string state and stores a string constructed from std::move(v).

  2. Throws: The exception thrown on allocation failure, or the exception thrown in the failed construction of the String.

constexpr basic_json(const char_type* first, const char_type* last, const allocator_type& a = allocator_type());
  1. Preconditions: [first, last) is a valid range.

  2. Effects: Sets node_ to the string state and stores a string containing characters in [first, last).

  3. Throws: The exception thrown on allocation failure, or the exception thrown in the failed construction of the String.

constexpr basic_json(const char_type* str, string_type::size_type count, const allocator_type& a = allocator_type());
  1. Preconditions: [str, str + count) is a valid range.

  2. Effects: Sets node_ to the string state and stores a string containing characters in [str, str + count).

  3. Throws: The exception thrown on allocation failure, or the exception thrown in the failed construction of the String.

constexpr explicit basic_json(const char_type* str, const allocator_type& a = allocator_type());
  1. Preconditions: str points to a null-terminated char_type sequence.

  2. Effects: Sets node_ to the string state and stores a string containing characters in [str, str + count), where count is the number of characters in the null-terminated sequence.

  3. Throws: The exception thrown on allocation failure, or the exception thrown in the failed construction of the String.

template<class StrLike>
  constexpr basic_json(const StrLike& str, const allocator_type& a = allocator_type());
  1. Constraints:

    • constructible_from<string_type, StrLike> is true, and

    • is_convertible_v<const KeyStrLike&, const key_char_type*> is false.

  2. Effects: Sets node_ to the string state and stores a string constructed from str.

  3. Throws: The exception thrown on allocation failure, or the exception thrown in by the construction of the String.

constexpr explicit basic_json(array_type arr, const allocator_type& a = allocator_type());
  1. Effects: Sets node_ to the array state and stores a dynamic array constructed from std::move(arr).

  2. Throws: The exception thrown on allocation failure, or the exception thrown by the construction of the Array.

constexpr explicit basic_json(object_type obj, const allocator_type& a = allocator_type());
  1. Effects: Sets node_ to the object state and stores a map constructed from std::move(obj).

  2. Throws: The exception thrown on allocation failure, or the exception thrown by the construction of the Object.

constexpr explicit basic_json(node_type&& n) noexcept;
  1. Effects: Constructs node_ from std::move(n) and sets n to the undefined state. The stored allocator in n is unchanged.

constexpr explicit basic_json(node_type&& n, const allocator_type& a) noexcept;
  1. Effects: Constructs node_ from std::move(a), sets it to the same state as n, and then sets n to the undefined state. The stored allocator in n is unchanged.

constexpr void swap(basic_json& rhs)
  noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
           allocator_traits<Allocator>::is_always_equal::value);
friend constexpr void swap(basic_json& lhs, basic_json& rhs) noexcept(noexcept(lhs.swap(rhs)));
  1. Effects: Swaps the values of the node_ member of both basic_json objects.

6.1.4.2. Observers
constexpr allocator_type get_allocator() const noexcept;
  1. Returns: The copy of the constructor stored in node_.

6.1.4.3. Slicing
constexpr operator node_type() && noexcept;
  1. Effects: Sets node_ to the undefined state.

  2. Returns: A node_type value holding the value of node_ before modification.

  3. Recommended practice: An implementation should emit a diagnostic message when the return value is discarded.
    [Note: Discarding the return value leaks memory if node_ was originally in the string, array, or object state. -- end note]

constexpr slice_type slice() noexcept;
  1. Effects: Equivalent to return slice_type(*this);.

constexpr const_slice_type slice() const noexcept;
  1. Effects: Equivalent to return const_slice_type(*this);.

6.1.5. Class template basic_json_slice

namespace std {
  template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
           class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
    class basic_json_slice {
    public:
      static constexpr bool has_integer  = HasInteger;
      static constexpr bool has_uinteger = HasUInteger;

      using node_type   = Node;
      using object_type = Object;
      using value_type  = Node;
      using array_type  = Array;
      using string_type = String;

      using slice_type       = basic_json_slice;
      using const_slice_type = basic_const_json_slice<Node, String, Array, Object,
                                                      HasInteger, HasUInteger>;
      using json_type        = basic_json<Node, String, Array, Object,
                                          HasInteger, HasUInteger>;

      using number_type   = node_type::number_type;
      using integer_type  = node_type::integer_type;
      using uinteger_type = node_type::uinteger_type;

      using char_type       = string_type::value_type;
      using map_node_type   = object_type::value_type;
      using allocator_type  = node_type::allocator_type;
      using key_string_type = object_type::key_type;
      using key_char_type   = key_string_type::value_type;

      constexpr basic_json_slice() noexcept = default;
      constexpr basic_json_slice(json_type& j) noexcept;
      constexpr basic_json_slice(node_type& n) noexcept;
      constexpr void swap(basic_json_slice& rhs) noexcept;
      friend constexpr void swap(basic_json_slice& lhs, basic_json_slice& rhs) noexcept;

      constexpr basic_json_slice& operator=(json_type& j) noexcept;
      constexpr basic_json_slice& operator=(node_type& n) noexcept;

      constexpr bool empty() const noexcept;
      constexpr bool undefined() const noexcept;
      constexpr bool string() const noexcept;
      constexpr bool null() const noexcept;
      constexpr bool boolean() const noexcept;
      constexpr bool number() const noexcept;
      constexpr bool object() const noexcept;
      constexpr bool array() const noexcept;
      constexpr bool integer() const noexcept;
      constexpr bool uinteger() const noexcept;

      constexpr explicit operator bool() const;
      constexpr explicit operator number_type() const;
      constexpr explicit operator nulljson_t() const;
      constexpr explicit operator const string_type&() const&;
      constexpr explicit operator const array_type&() const&;
      constexpr explicit operator const object_type&() const&;
      constexpr explicit operator integer_type() const;
      constexpr explicit operator uinteger_type() const;
      template<integral I>
        constexpr basic_const_json_slice operator[](I pos) const;
      constexpr basic_const_json_slice operator[](const key_string_type& k) const;
      template<class KeyStrLike>
        constexpr basic_const_json_slice operator[](const KeyStrLike& k) const;
      constexpr basic_const_json_slice operator[](const key_char_type* ks) const;

      constexpr void reset() noexcept;
      constexpr basic_json_slice& operator=(nulljson_t);
      template<class T>
        constexpr basic_json_slice& operator=(T n);
      constexpr basic_json_slice& operator=(const string_type& str);
      constexpr basic_json_slice& operator=(string_type&& str);
      constexpr basic_json_slice& operator=(const char_type* str);
      template<class StrLike>
        constexpr basic_json_slice& operator=(const StrLike& str);
      template<integral I>
        constexpr basic_json_slice operator[](I pos);
      constexpr basic_json_slice operator[](const key_string_type& k);
      template<class KeyStrLike>
        constexpr basic_json_slice operator[](const KeyStrLike& k);
      constexpr basic_json_slice operator[](const key_char_type* ks);

    private:
      node_type* node_ = nullptr;    // exposition only
    };

  template<class Node, class String, class Array, class Object,
           bool HasInteger, bool HasUInteger, class Allocator>
    struct uses_allocator<basic_json_slice<Node, String, Array, Object,
                                              HasInteger, HasUInteger>, Allocator2> :
      false_type {};
}
  1. Every specialization of basic_json_slice is a trivally copyable class that models semiregular.

  2. A basic_json_slice is considered valid if its node_ member points to a node_type object within its lifetime.
    [Note: A default-constructed basic_json_slice is not valid. -- end note]

  3. [Note: The default constructor of basic_json_slice is intentionally used for default arguments. -- end note]

  4. Whenever basic_json_slice is valid, its referenced node is the object denoted by *node_.

  5. [Note: The uses_allocator partial specialization indicates that basic_json_slice should not be uses-allocator constructed. -- end note]

6.1.5.1. Construction and swap
constexpr basic_json_slice(json_type& j) noexcept;
  1. Effects: Initializes node_ with addressof(j.node_).

constexpr basic_json_slice(node_type& n) noexcept;
  1. Effects: Initializes node_ with addressof(n).

constexpr void swap(basic_json_slice& rhs) noexcept;
friend constexpr void swap(basic_json_slice& lhs, basic_json_slice& rhs) noexcept;
  1. Effects: Swaps the values of the node_ member of both basic_json_slice objects.

6.1.5.2. Rebinding
constexpr basic_json_slice& operator=(json_type& j) noexcept;
  1. Effects: Equivalent to return *this = basic_json_slice{j};.

constexpr basic_json_slice& operator=(node_type& n) noexcept;
  1. Effects: Equivalent to return *this = basic_json_slice{n};.

6.1.5.3. Observers
constexpr bool empty() const noexcept;
constexpr bool undefined() const noexcept;
constexpr bool string() const noexcept;
constexpr bool null() const noexcept;
constexpr bool boolean() const noexcept;
constexpr bool number() const noexcept;
constexpr bool object() const noexcept;
constexpr bool array() const noexcept;
constexpr bool integer() const noexcept;
constexpr bool uinteger() const noexcept;

constexpr explicit operator bool() const;
constexpr explicit operator number_type() const;
constexpr explicit operator nulljson_t() const;
constexpr explicit operator const string_type&() const&;
constexpr explicit operator const array_type&() const&;
constexpr explicit operator const object_type&() const&;
constexpr explicit operator integer_type() const;
constexpr explicit operator uinteger_type() const;
template<integral I>
  constexpr basic_const_json_slice operator[](I pos) const;
constexpr basic_const_json_slice operator[](const key_string_type& k) const;
template<class KeyStrLike>
  constexpr basic_const_json_slice operator[](const KeyStrLike& k) const;
constexpr basic_const_json_slice operator[](const key_char_type* ks) const;
  1. Every observer function of basic_json_slice has the same constraints and semantics as converting the basic_json_slice to its const_slice_type and then calling the corresponding member function of basic_const_json_slice.

6.1.5.4. Modifiers
  1. Except for reset, a modifier function of basic_json_slice may change the state of the referenced node, but only if the node was in the undefined state. A json_error exception is thrown if one attempts to change the established non-undefined state of the node.

constexpr void reset() noexcept;
  1. Preconditions: *this is valid.

  2. Effects: Sets the referenced node to the undefined state.

constexpr basic_json_slice& operator=(nulljson_t);
  1. Preconditions: *this is valid.

  2. Effects:

    • If the referenced node is in the undefined state, sets it to the null state.

    • Otherwise, if the referenced node is in the null state, does nothing.

  3. Returns: *this.

  4. Throws: A json_error exception constructed from json_errc::not_undefined_or_null if the referenced node is not in the undefined or the null state.

template<class T>
  constexpr basic_json_slice& operator=(T n);
  1. Constraints: is_arithmetic_v<T> is true.

  2. Preconditions: *this is valid.

  3. Effects:

    • If the referenced node is in the undefined state, performs *node_ = json_type{n}.

    • Otherwise, if

      • T is bool, n is true, and the referenced node is in the true state, or

      • T is bool, n is false, and the referenced node is in the false state,

      then does nothing.

    • Otherwise, if HasInteger and signed_integral<T> are both true and the referenced node is in the signed integral state, replaces the stored signed integer value with n.

    • Otherwise, if HasUInteger and unsigned_integral<T> are both true and the referenced node is in the unsigned integral state, replaces the stored unsigned integer value with n.

    • Otherwise, the referenced node is in the floating-point state, replaces the stored floating-point value with static_cast<number_type>(n).

    • Otherwise, throws a json_error exception constructed from json_errc::not_undefined_or_number;

  4. Returns: *this.

constexpr basic_json_slice& operator=(const string_type& str);
  1. Preconditions: *this is valid.

  2. Effects:

    • If the referenced node is in the undefined state, sets it to the string state and stores a string constructed from str.

    • Otherwise, if the referenced node is in the string state, assigns str to the stored string.

  3. Returns: *this.

  4. Throws:

    • A json_error exception constructed from json_errc::not_undefined_or_string if the referenced node is not in the undefined or the string state.

    • Otherwise, the exception thrown by the allocation, construction, or assignment of the String.

constexpr basic_json_slice& operator=(string_type&& str);
  1. Preconditions: *this is valid.

  2. Effects:

    • If the referenced node is in the undefined state, sets it to the string state and stores a string constructed from std::move(str).

    • Otherwise, if the referenced node is in the string state, assigns std::move(str) to the stored string.

  3. Returns: *this.

  4. Throws:

    • A json_error exception constructed from json_errc::not_undefined_or_string if the referenced node is not in the undefined or the string state.

    • Otherwise, the exception thrown by the allocation, construction, or assignment of the String.

constexpr basic_json_slice& operator=(const char_type* str);
  1. Preconditions: *this is valid. If the referenced node is in the undefined or the string state, str points to a null-terminated char_type sequence.

  2. Effects:

    • If the referenced node is in the undefined state, sets it to the string state and stores a string containing characters from str until str + count, where count is the number of characters in the null-terminated sequence.

    • Otherwise, if the referenced node is in the string state, replaces the contents of str with that of the null-terminated sequence.

  3. Returns: *this.

  4. Throws:

    • A json_error exception constructed from json_errc::not_undefined_or_string if the referenced node is not in the undefined or the string state.

    • Otherwise, the exception thrown by the allocation, construction, or assignment of the String.

template<class StrLike>
  constexpr basic_json_slice& operator=(const StrLike& str);
  1. Constraints:

    • constructible_from<string_type, StrLike> is true, and

    • is_convertible_v<const KeyStrLike&, const key_char_type*> is false.

  2. Preconditions: *this is valid.

  3. Effects:

    • If the referenced node is in the undefined state, sets it to the string state and stores a string constructed from str.

    • Otherwise, if the referenced node is in the string state, assigns str to the stored string.

  4. Returns: *this.

  5. Throws:

    • A json_error exception constructed from json_errc::not_undefined_or_string if the referenced node is not in the undefined or the string state.

    • Otherwise, the exception thrown by the allocation, construction, or assignment of the String.

template<integral I>
  constexpr basic_json_slice operator[](I pos);
  1. Preconditions: *this is valid. If the referenced node is in the array state, pos is less than the length of the stored dynamic array.

  2. Returns: A basic_json_slice referencing the node at offset pos of the stored dynamic array if the referenced node is in the array state.

  3. Throws: A json_error exception constructed from json_errc::nonarray_indexing if the referenced node is not in the array state.

constexpr basic_json_slice operator[](const key_string_type& k);
template<class KeyStrLike>
  constexpr basic_json_slice operator[](const KeyStrLike& k);
constexpr basic_json_slice operator[](const key_char_type* ks);
  1. Constraints: For the template overload:

    • Object is transparently comparable, and

    • is_convertible_v<const KeyStrLike&, const key_char_type*> is false.

  2. Preconditions: *this is valid. For the overload taking a const key_char_type*, if the referenced node is in the object state, ks points to a null-terminated key_char_type sequence.

  3. For the overload taking a const key_char_type*, let k be a key_string_type value containing characters in [ks, ks + count), where count is the number of characters in the null-terminated sequence.

  4. Effects: If the referenced node is in the object state and no element with key k is found in its stored map, inserts an element with key k and a value-initialized node into the stored map.

  5. Returns:

    • If the referenced node is in the object state and an element with key k is found in its stored map, a basic_json_slice referencing the mapped value of that element.

    • Otherwise, if a new element e is inserted, a basic_json_slice, a basic_json_slice the mapped value of the inserted element.

  6. Throws:

    • A json_error exception constructed from json_errc::nonobject_indexing the referenced node is not in the object state.

    • Otherwise, the exception thrown when looking up the key, or by the allocation or construction of the inserted map element.

6.1.6. Class template basic_const_json_slice

namespace std {
  template<class Node = basic_json_node<>, class String = string, class Array = vector<Node>,
           class Object = map<String, Node>, bool HasInteger = true, bool HasUInteger = true>
    class basic_const_json_slice {
    public:
      static constexpr bool has_integer  = HasInteger;
      static constexpr bool has_uinteger = HasUInteger;

      using node_type   = Node;
      using object_type = Object;
      using value_type  = Node;
      using array_type  = Array;
      using string_type = String;

      using slice_type       = basic_json_slice<Node, String, Array, Object,
                                                HasInteger, HasUInteger>;
      using const_slice_type = basic_const_json_slice;
      using json_type        = basic_json<Node, String, Array, Object,
                                          HasInteger, HasUInteger>;

      using number_type   = node_type::number_type;
      using integer_type  = node_type::integer_type;
      using uinteger_type = node_type::uinteger_type;

      using char_type       = string_type::value_type;
      using map_node_type   = object_type::value_type;
      using allocator_type  = node_type::allocator_type;
      using key_string_type = object_type::key_type;
      using key_char_type   = key_string_type::value_type;

      constexpr basic_const_json_slice() noexcept = default;
      constexpr basic_const_json_slice(const json_type& j) noexcept;
      constexpr basic_const_json_slice(const node_type& n) noexcept;
      constexpr basic_const_json_slice(const slice_type& s) noexcept;
      constexpr void swap(basic_const_json_slice& rhs) noexcept;
      friend constexpr void swap(basic_const_json_slice& lhs,
                                 basic_const_json_slice& rhs) noexcept;

      constexpr bool undefined() const noexcept;
      constexpr bool string() const noexcept;
      constexpr bool null() const noexcept;
      constexpr bool boolean() const noexcept;
      constexpr bool number() const noexcept;
      constexpr bool object() const noexcept;
      constexpr bool array() const noexcept;
      constexpr bool integer() const noexcept;
      constexpr bool uinteger() const noexcept;

      constexpr explicit operator bool() const;
      constexpr explicit operator number_type() const;
      constexpr explicit operator nulljson_t() const;
      constexpr explicit operator const string_type&() const&;
      constexpr explicit operator const array_type&() const&;
      constexpr explicit operator const object_type&() const&;
      constexpr explicit operator integer_type() const;
      constexpr explicit operator uinteger_type() const;
      template<integral I>
        constexpr basic_const_json_slice operator[](I pos) const;
      constexpr basic_const_json_slice operator[](const key_string_type& k) const;
      template<class KeyStrLike>
        constexpr basic_const_json_slice operator[](const KeyStrLike& k) const;
      constexpr basic_const_json_slice operator[](const key_char_type* ks) const;

    private:
      const node_type* node_ = nullptr;  // exposition only
    };

  template<class Node, class String, class Array, class Object,
           bool HasInteger, bool HasUInteger, class Allocator>
    struct uses_allocator<basic_const_json_slice<Node, String, Array, Object,
                                                    HasInteger, HasUInteger>, Allocator2> :
      false_type {};
}
  1. Every specialization of basic_const_json_slice is a trivally copyable class that models semiregular.

  2. A basic_const_json_slice is considered valid if its node_ member points to a node_type object within its lifetime.
    [Note: A default-constructed basic_const_json_slice is not valid. -- end note]

  3. [Note: The default constructor of basic_const_json_slice is intentionally used for default arguments. -- end note]

  4. Whenever basic_const_json_slice is valid, its referenced node is the object denoted by *node_.

  5. [Note: The uses_allocator partial specialization indicates that basic_const_json_slice should not be uses-allocator constructed. -- end note]

6.1.6.1. Construction and swap
constexpr basic_const_json_slice(const json_type& j) noexcept;
  1. Effects: Initializes node_ with addressof(j.node_).

constexpr basic_const_json_slice(const node_type& n) noexcept;
  1. Effects: Initializes node_ with addressof(n).

constexpr basic_const_json_slice(const slice_type& s) noexcept;
  1. Effects: Initializes node_ with s.node_.
    [Note: *this and s reference the same node after construction if s is valid. -- end note]

constexpr void swap(basic_const_json_slice& rhs) noexcept;
friend constexpr void swap(basic_const_json_slice& lhs,
                           basic_const_json_slice& rhs) noexcept;
  1. Effects: Swaps the values of the node_ member of both basic_const_json_slice objects.

6.1.6.2. Observers
constexpr bool empty() const noexcept;
  1. Returns: bool(node_).
    [Note: *this is not valid when empty() returns true. -- end note]

constexpr bool undefined() const noexcept;
  1. Preconditions: *this is valid.

  2. Returns: true if the referenced node is in the undefined state, false otherwise.

constexpr bool string() const noexcept;
  1. Preconditions: *this is valid.

  2. Returns: true if the referenced node is in the string state, false otherwise.

constexpr bool null() const noexcept;
  1. Preconditions: *this is valid.

  2. Returns: true if the referenced node is in the null state, false otherwise.

constexpr bool boolean() const noexcept;
  1. Preconditions: *this is valid.

  2. Returns: true if the referenced node is in the true of the false state, false otherwise.

constexpr bool number() const noexcept;
  1. Preconditions: *this is valid.

  2. Returns: true if the referenced node is in the number state, false otherwise.

constexpr bool object() const noexcept;
  1. Preconditions: *this is valid.

  2. Returns: true if the referenced node is in the object state, false otherwise.

constexpr bool array() const noexcept;
  1. Preconditions: *this is valid.

  2. Returns: true if the referenced node is in the array state, false otherwise.

constexpr bool integer() const noexcept;
  1. Constraints: HasInteger is true.

  2. Preconditions: *this is valid.

  3. Returns: true if the referenced node is in the signed integral state, false otherwise.

constexpr bool uinteger() const noexcept;
  1. Constraints: HasUInteger is true.

  2. Preconditions: *this is valid.

  3. Returns: true if the referenced node is in the unsigned integral state, false otherwise.

constexpr explicit operator bool() const;
  1. Preconditions: *this is valid.

  2. Returns: true if the referenced node is in the true state, false if the referenced node is in the false state.

  3. Throws: A json_error exception constructed from json_errc::not_boolean if the referenced node is not in the true or the false state.

constexpr explicit operator number_type() const;
  1. Preconditions: *this is valid.

  2. Returns: The stored numeric value converted to number_type if the referenced node is in the floating-point, the signed integral, or the unsigned integral state.

  3. Throws: A json_error exception constructed from json_errc::not_number if the referenced node is not in the floating-point, the signed integral, or the unsigned integral state.

constexpr explicit operator nulljson_t() const;
  1. Preconditions: *this is valid.

  2. Returns: nulljson_t{} if the referenced node is in the null state.

  3. Throws: A json_error exception constructed from json_errc::not_null if the referenced node is not in the null state.

constexpr explicit operator const string_type&() const&;
  1. Preconditions: *this is valid.

  2. Returns: The reference to the stored string if the referenced node is in the string state.

  3. Throws: A json_error exception constructed from json_errc::not_string if the referenced node is not in the string state.

constexpr explicit operator const array_type&() const&;
  1. Preconditions: *this is valid.

  2. Returns: The reference to the stored dynamic array if the referenced node is in the array state.

  3. Throws: A json_error exception constructed from json_errc::not_array if the referenced node is not in the array state.

constexpr explicit operator const object_type&() const&;
  1. Preconditions: *this is valid.

  2. Returns: The reference to the stored map if the referenced node is in the object state.

  3. Throws: A json_error exception constructed from json_errc::not_object if the referenced node is not in the object state.

constexpr explicit operator integer_type() const;
  1. Constraints: HasInteger is true.

  2. Preconditions: *this is valid.

  3. Returns: The stored integer value if the referenced node is in the signed integral state.

  4. Throws: A json_error exception constructed from json_errc::not_integer if the referenced node is not in the signed integral state.

constexpr explicit operator uinteger_type() const;
  1. Constraints: HasUInteger is true.

  2. Preconditions: *this is valid.

  3. Returns: The stored integer value if the referenced node is in the unsigned integral state.

  4. Throws: A json_error exception constructed from json_errc::not_uinteger if the referenced node is not in the unsigned integral state.

template<integral I>
  constexpr basic_const_json_slice operator[](I pos) const;
  1. Preconditions: *this is valid. If the referenced node is in the array state, pos is less than the length of the stored dynamic array.

  2. Returns: A basic_const_json_slice referencing the node at offset pos of the stored dynamic array if the referenced node is in the array state.

  3. Throws: A json_error exception constructed from json_errc::nonarray_indexing if the referenced node is not in the array state.

constexpr basic_const_json_slice operator[](const key_string_type& k) const;
template<class KeyStrLike>
  constexpr basic_const_json_slice operator[](const KeyStrLike& k) const;
constexpr basic_const_json_slice operator[](const key_char_type* ks) const;
  1. Constraints: For the template overload:

    • Object is transparently comparable, and

    • is_convertible_v<const KeyStrLike&, const key_char_type*> is false.

  2. Preconditions: *this is valid. For the overload taking a const key_char_type*, if the referenced node is in the object state, ks points to a null-terminated key_char_type sequence.

  3. For the overload taking a const key_char_type*, let k be a key_string_type value containing characters in [ks, ks + count), where count is the number of characters in the null-terminated sequence.

  4. Returns: If the referenced node is in the object state and an element with key k is found in the stored map, a basic_const_json_slice referencing the mapped value of that element.

  5. Throws:

    • A json_error exception constructed from json_errc::nonobject_indexing if the referenced node is not in the object state.

    • Otherwise, the exception thrown when looking up the key or json_error exception constructed from json_errc::key_not_found if no element with key k is found.

6.1.7. Error code enumeration json_errc

namespace std {
  enum class json_errc : unspecified {
    // invalid access
    is_empty                 = see below,
    is_undefined             = see below,
    not_null                 = see below,
    not_boolean              = see below,
    not_number               = see below,
    not_integer              = see below,
    not_uinteger             = see below,
    not_string               = see below,
    not_array                = see below,
    not_object               = see below,
    nonarray_indexing        = see below,
    nonobject_indexing       = see below,
    key_not_found            = see below,

    // invalid modification
    not_undefined_or_null    = see below,
    not_undefined_or_boolean = see below,
    not_undefined_or_number  = see below,
    not_undefined_or_string  = see below,
  };
}
  1. The enumeration type json_errc is used for reporting JSON-related errors.

  2. The underlying type of json_errc is at least as wide as int. Each enumerator specified above has a distinct, nonzero value.

6.1.8. Class json_error

namespace std {
  class json_error : public runtime_error {
  public:
    json_error(json_errc ec);
    json_errc code() const noexcept { return code_; }

  private:
    json_errc code_;
  };
}
  1. The class format_error defines the exception type that reports errors from the JSON-related operations.

json_error(json_errc ec);
  1. Postconditions: code_ == ec.

6.2. Feature-test macro

Add a new feature-test macro to [version.syn].

#define __cpp_lib_json                              202ymmL // also in <json>

7. Acknowledgements

Thanks to An Jiang for the information about polymorphic_allocator, and the suggestions for nulljson_t and constructor.

Thanks to ykiko for simplifying code using fold expressions.

Thanks to YanzuoLiu for patiently reviewing the code and providing a graceful implementation of constructing objects.

References

Informative References

[3917]
Daniel Krügler. Validity of allocator<void> and possibly polymorphic_allocator<void> should be clarified. April 2023. URL: https://cplusplus.github.io/LWG/issue3917
[LFTSv3]
Thomas Köppe. Working Draft, C++ Extensions for Library Fundamentals, Version 3. December 17, 2022. URL: https://cplusplus.github.io/fundamentals-ts/v3.html
[RFC8259]
IETF. The JavaScript Object Notation (JSON) Data Interchange Format. December 2017. URL: https://www.rfc-editor.org/rfc/rfc8259