Skip to content
Snippets Groups Projects
json.hpp 363 KiB
Newer Older
        break;
      }

      default:
      {
        JSON_THROW(
            std::domain_error("cannot use construct with iterators from " +
                              first.m_object->type_name()));
      }
    }

    assert_invariant();
  }

  /*!
  @brief construct a JSON value given an input stream

  @param[in,out] i  stream to read a serialized JSON value from
  @param[in] cb a parser callback function of type @ref parser_callback_t
  which is used to control the deserialization by filtering unwanted values
  (optional)

  @complexity Linear in the length of the input. The parser is a predictive
  LL(1) parser. The complexity can be higher if the parser callback function
  @a cb has a super-linear complexity.

  @note A UTF-8 byte order mark is silently ignored.

  @deprecated This constructor is deprecated and will be removed in version
    3.0.0 to unify the interface of the library. Deserialization will be
    done by stream operators or by calling one of the `parse` functions,
    e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls
    like `json j(i);` for an input stream @a i need to be replaced by
    `json j = json::parse(i);`. See the example below.

  @liveexample{The example below demonstrates constructing a JSON value from
  a `std::stringstream` with and without callback
  function.,basic_json__istream}

  @since version 2.0.0, deprecated in version 2.0.3, to be removed in
         version 3.0.0
  */
  JSON_DEPRECATED
  explicit basic_json(std::istream &i, const parser_callback_t cb = nullptr)
  {
    *this = parser(i, cb).parse();
    assert_invariant();
  }

  ///////////////////////////////////////
  // other constructors and destructor //
  ///////////////////////////////////////

  /*!
  @brief copy constructor

  Creates a copy of a given JSON value.

  @param[in] other  the JSON value to copy

  @complexity Linear in the size of @a other.

  @requirement This function helps `basic_json` satisfying the
  [Container](http://en.cppreference.com/w/cpp/concept/Container)
  requirements:
  - The complexity is linear.
  - As postcondition, it holds: `other == basic_json(other)`.

  @throw std::bad_alloc if allocation for object, array, or string fails.

  @liveexample{The following code shows an example for the copy
  constructor.,basic_json__basic_json}

  @since version 1.0.0
  */
  basic_json(const basic_json &other) : m_type(other.m_type)
  {
    // check of passed value is valid
    other.assert_invariant();

    switch (m_type)
    {
      case value_t::object:
      {
        m_value = *other.m_value.object;
        break;
      }

      case value_t::array:
      {
        m_value = *other.m_value.array;
        break;
      }

      case value_t::string:
      {
        m_value = *other.m_value.string;
        break;
      }

      case value_t::boolean:
      {
        m_value = other.m_value.boolean;
        break;
      }

      case value_t::number_integer:
      {
        m_value = other.m_value.number_integer;
        break;
      }

      case value_t::number_unsigned:
      {
        m_value = other.m_value.number_unsigned;
        break;
      }

      case value_t::number_float:
      {
        m_value = other.m_value.number_float;
        break;
      }

      default:
      {
        break;
      }
    }

    assert_invariant();
  }

  /*!
  @brief move constructor

  Move constructor. Constructs a JSON value with the contents of the given
  value @a other using move semantics. It "steals" the resources from @a
  other and leaves it as JSON null value.

  @param[in,out] other  value to move to this object

  @post @a other is a JSON null value

  @complexity Constant.

  @liveexample{The code below shows the move constructor explicitly called
  via std::move.,basic_json__moveconstructor}

  @since version 1.0.0
  */
  basic_json(basic_json &&other) noexcept : m_type(std::move(other.m_type)),
                                            m_value(std::move(other.m_value))
  {
    // check that passed value is valid
    other.assert_invariant();

    // invalidate payload
    other.m_type = value_t::null;
    other.m_value = {};

    assert_invariant();
  }

  /*!
  @brief copy assignment

  Copy assignment operator. Copies a JSON value via the "copy and swap"
  strategy: It is expressed in terms of the copy constructor, destructor,
  and the swap() member function.

  @param[in] other  value to copy from

  @complexity Linear.

  @requirement This function helps `basic_json` satisfying the
  [Container](http://en.cppreference.com/w/cpp/concept/Container)
  requirements:
  - The complexity is linear.

  @liveexample{The code below shows and example for the copy assignment. It
  creates a copy of value `a` which is then swapped with `b`. Finally\, the
  copy of `a` (which is the null value after the swap) is
  destroyed.,basic_json__copyassignment}

  @since version 1.0.0
  */
  reference &operator=(basic_json other) noexcept(
      std::is_nothrow_move_constructible<value_t>::value
          and std::is_nothrow_move_assignable<value_t>::value
              and std::is_nothrow_move_constructible<json_value>::value
                  and std::is_nothrow_move_assignable<json_value>::value)
  {
    // check that passed value is valid
    other.assert_invariant();

    using std::swap;
    swap(m_type, other.m_type);
    swap(m_value, other.m_value);

    assert_invariant();
    return *this;
  }

  /*!
  @brief destructor

  Destroys the JSON value and frees all allocated memory.
  @complexity Linear.
  @requirement This function helps `basic_json` satisfying the
  [Container](http://en.cppreference.com/w/cpp/concept/Container)
  requirements:
  - The complexity is linear.
  - All stored elements are destroyed and all memory is freed.
  @since version 1.0.0
  */
  ~basic_json()
  {
    assert_invariant();
    switch (m_type)
    {
      case value_t::object:
      {
        AllocatorType<object_t> alloc;
        alloc.destroy(m_value.object);
        alloc.deallocate(m_value.object, 1);
        break;
      }
      case value_t::array:
      {
        AllocatorType<array_t> alloc;
        alloc.destroy(m_value.array);
        alloc.deallocate(m_value.array, 1);
        break;
      }
      case value_t::string:
      {
        AllocatorType<string_t> alloc;
        alloc.destroy(m_value.string);
        alloc.deallocate(m_value.string, 1);
        break;
      }
      default:
      {
        // all other types need no specific destructor
        break;
      }
public:
  ///////////////////////
  // object inspection //
  ///////////////////////
  /// @name object inspection
  /// Functions to inspect the type of a JSON value.
  /// @{
  /*!
  @brief serialization
  Serialization function for JSON values. The function tries to mimic
  Python's `json.dumps()` function, and currently supports its @a indent
  parameter.
  @param[in] indent If indent is nonnegative, then array elements and object
  members will be pretty-printed with that indent level. An indent level of
  `0` will only insert newlines. `-1` (the default) selects the most compact
  representation.
  @return string containing the serialization of the JSON value
  @complexity Linear.
  @liveexample{The following example shows the effect of different @a indent
  parameters to the result of the serialization.,dump}
  @see https://docs.python.org/2/library/json.html#json.dump
  @since version 1.0.0
  */
  string_t dump(const int indent = -1) const
  {
    std::stringstream ss;
    // fix locale problems
    ss.imbue(std::locale::classic());
    // 6, 15 or 16 digits of precision allows round-trip IEEE 754
    // string->float->string, string->double->string or string->long
    // double->string; to be safe, we read this value from
    // std::numeric_limits<number_float_t>::digits10
    ss.precision(std::numeric_limits<double>::digits10);
    if (indent >= 0)
    {
      dump(ss, true, static_cast<unsigned int>(indent));
    }
    else
    {
      dump(ss, false, 0);
    }
    return ss.str();
  }
  /*!
  @brief return the type of the JSON value (explicit)
  Return the type of the JSON value as a value from the @ref value_t
  enumeration.
  @return the type of the JSON value
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `type()` for all JSON
  types.,type}
  @since version 1.0.0
  */
  constexpr value_t type() const noexcept { return m_type; }
  /*!
  @brief return whether type is primitive
  This function returns true iff the JSON type is primitive (string, number,
  boolean, or null).
  @return `true` if type is primitive (string, number, boolean, or null),
  `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_primitive()` for all JSON
  types.,is_primitive}
  @sa @ref is_structured() -- returns whether JSON value is structured
  @sa @ref is_null() -- returns whether JSON value is `null`
  @sa @ref is_string() -- returns whether JSON value is a string
  @sa @ref is_boolean() -- returns whether JSON value is a boolean
  @sa @ref is_number() -- returns whether JSON value is a number
  @since version 1.0.0
  */
  constexpr bool is_primitive() const noexcept
  {
    return is_null() or is_string() or is_boolean() or is_number();
  }
  /*!
  @brief return whether type is structured
  This function returns true iff the JSON type is structured (array or
  object).
  @return `true` if type is structured (array or object), `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_structured()` for all JSON
  types.,is_structured}
  @sa @ref is_primitive() -- returns whether value is primitive
  @sa @ref is_array() -- returns whether value is an array
  @sa @ref is_object() -- returns whether value is an object
  @since version 1.0.0
  */
  constexpr bool is_structured() const noexcept
  {
    return is_array() or is_object();
  }
  /*!
  @brief return whether value is null
  This function returns true iff the JSON value is null.
  @return `true` if type is null, `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_null()` for all JSON
  types.,is_null}
  @since version 1.0.0
  */
  constexpr bool is_null() const noexcept { return m_type == value_t::null; }
  /*!
  @brief return whether value is a boolean
  This function returns true iff the JSON value is a boolean.
  @return `true` if type is boolean, `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_boolean()` for all JSON
  types.,is_boolean}
  @since version 1.0.0
  */
  constexpr bool is_boolean() const noexcept
  {
    return m_type == value_t::boolean;
  }
  /*!
  @brief return whether value is a number
  This function returns true iff the JSON value is a number. This includes
  both integer and floating-point values.
  @return `true` if type is number (regardless whether integer, unsigned
  integer or floating-type), `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_number()` for all JSON
  types.,is_number}
  @sa @ref is_number_integer() -- check if value is an integer or unsigned
  integer number
  @sa @ref is_number_unsigned() -- check if value is an unsigned integer
  number
  @sa @ref is_number_float() -- check if value is a floating-point number
  @since version 1.0.0
  */
  constexpr bool is_number() const noexcept
  {
    return is_number_integer() or is_number_float();
  }
  /*!
  @brief return whether value is an integer number
  This function returns true iff the JSON value is an integer or unsigned
  integer number. This excludes floating-point values.
  @return `true` if type is an integer or unsigned integer number, `false`
  otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_number_integer()` for all
  JSON types.,is_number_integer}
  @sa @ref is_number() -- check if value is a number
  @sa @ref is_number_unsigned() -- check if value is an unsigned integer
  number
  @sa @ref is_number_float() -- check if value is a floating-point number
  @since version 1.0.0
  */
  constexpr bool is_number_integer() const noexcept
  {
    return m_type == value_t::number_integer or
           m_type == value_t::number_unsigned;
  }
  /*!
  @brief return whether value is an unsigned integer number
  This function returns true iff the JSON value is an unsigned integer
  number. This excludes floating-point and (signed) integer values.
  @return `true` if type is an unsigned integer number, `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_number_unsigned()` for all
  JSON types.,is_number_unsigned}
  @sa @ref is_number() -- check if value is a number
  @sa @ref is_number_integer() -- check if value is an integer or unsigned
  integer number
  @sa @ref is_number_float() -- check if value is a floating-point number
  @since version 2.0.0
  */
  constexpr bool is_number_unsigned() const noexcept
  {
    return m_type == value_t::number_unsigned;
  }
  /*!
  @brief return whether value is a floating-point number
  This function returns true iff the JSON value is a floating-point number.
  This excludes integer and unsigned integer values.
  @return `true` if type is a floating-point number, `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_number_float()` for all
  JSON types.,is_number_float}
  @sa @ref is_number() -- check if value is number
  @sa @ref is_number_integer() -- check if value is an integer number
  @sa @ref is_number_unsigned() -- check if value is an unsigned integer
  number
  @since version 1.0.0
  */
  constexpr bool is_number_float() const noexcept
  {
    return m_type == value_t::number_float;
  }
  /*!
  @brief return whether value is an object
  This function returns true iff the JSON value is an object.
  @return `true` if type is object, `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_object()` for all JSON
  types.,is_object}
  @since version 1.0.0
  */
  constexpr bool is_object() const noexcept
  {
    return m_type == value_t::object;
  }
  /*!
  @brief return whether value is an array
  This function returns true iff the JSON value is an array.
  @return `true` if type is array, `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_array()` for all JSON
  types.,is_array}
  @since version 1.0.0
  */
  constexpr bool is_array() const noexcept { return m_type == value_t::array; }
  /*!
  @brief return whether value is a string
  This function returns true iff the JSON value is a string.
  @return `true` if type is string, `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_string()` for all JSON
  types.,is_string}
  @since version 1.0.0
  */
  constexpr bool is_string() const noexcept
  {
    return m_type == value_t::string;
  }
  /*!
  @brief return whether value is discarded
  This function returns true iff the JSON value was discarded during parsing
  with a callback function (see @ref parser_callback_t).
  @note This function will always be `false` for JSON values after parsing.
  That is, discarded values can only occur during parsing, but will be
  removed when inside a structured value or replaced by null in other cases.
  @return `true` if type is discarded, `false` otherwise.
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies `is_discarded()` for all JSON
  types.,is_discarded}
  @since version 1.0.0
  */
  constexpr bool is_discarded() const noexcept
  {
    return m_type == value_t::discarded;
  }
  /*!
  @brief return the type of the JSON value (implicit)
  Implicitly return the type of the JSON value as a value from the @ref
  value_t enumeration.
  @return the type of the JSON value
  @complexity Constant.
  @exceptionsafety No-throw guarantee: this member function never throws
  exceptions.
  @liveexample{The following code exemplifies the @ref value_t operator for
  all JSON types.,operator__value_t}
  @since version 1.0.0
  */
  constexpr operator value_t() const noexcept { return m_type; }
private:
  //////////////////
  // value access //
  //////////////////
  /// get an object (explicit)
  template <
      class T,
      typename std::enable_if<
          std::is_convertible<typename object_t::key_type,
                              typename T::key_type>::value and
              std::is_convertible<basic_json_t, typename T::mapped_type>::value,
          int>::type = 0>
  T get_impl(T * /*unused*/) const
  {
    if (is_object())
    {
      return T(m_value.object->begin(), m_value.object->end());
    }
    JSON_THROW(std::domain_error("type must be object, but is " + type_name()));
  }
  /// get an object (explicit)
  object_t get_impl(object_t * /*unused*/) const
  {
    if (is_object())
    {
      return *(m_value.object);
    }
    JSON_THROW(std::domain_error("type must be object, but is " + type_name()));
  }
  /// get an array (explicit)
  template <
      class T,
      typename std::enable_if<
          std::is_convertible<basic_json_t, typename T::value_type>::value and
              not std::is_same<basic_json_t, typename T::value_type>::value and
              not std::is_arithmetic<T>::value and
              not std::is_convertible<std::string, T>::value and
              not has_mapped_type<T>::value,
          int>::type = 0>
  T get_impl(T * /*unused*/) const
  {
    if (is_array())
    {
      T to_vector;
      std::transform(
          m_value.array->begin(), m_value.array->end(),
          std::inserter(to_vector, to_vector.end()),
          [](basic_json i) { return i.get<typename T::value_type>(); });
      return to_vector;
    }
    JSON_THROW(std::domain_error("type must be array, but is " + type_name()));
  }
  /// get an array (explicit)
  template <class T, typename std::enable_if<
                         std::is_convertible<basic_json_t, T>::value and
                             not std::is_same<basic_json_t, T>::value,
                         int>::type = 0>
  std::vector<T> get_impl(std::vector<T> * /*unused*/) const
  {
    if (is_array())
    {
      std::vector<T> to_vector;
      to_vector.reserve(m_value.array->size());
      std::transform(m_value.array->begin(), m_value.array->end(),
                     std::inserter(to_vector, to_vector.end()),
                     [](basic_json i) { return i.get<T>(); });
      return to_vector;
    }
    JSON_THROW(std::domain_error("type must be array, but is " + type_name()));
  }
  /// get an array (explicit)
  template <class T,
            typename std::enable_if<
                std::is_same<basic_json, typename T::value_type>::value and
                    not has_mapped_type<T>::value,
                int>::type = 0>
  T get_impl(T * /*unused*/) const
  {
    if (is_array())
    {
      return T(m_value.array->begin(), m_value.array->end());
    }

    JSON_THROW(std::domain_error("type must be array, but is " + type_name()));
  }

  /// get an array (explicit)
  array_t get_impl(array_t * /*unused*/) const
  {
    if (is_array())
    {
      return *(m_value.array);
    }

    JSON_THROW(std::domain_error("type must be array, but is " + type_name()));
  }

  /// get a string (explicit)
  template <typename T,
            typename std::enable_if<std::is_convertible<string_t, T>::value,
                                    int>::type = 0>
  T get_impl(T * /*unused*/) const
  {
    if (is_string())
    {
      return *m_value.string;
    }

    JSON_THROW(std::domain_error("type must be string, but is " + type_name()));
  }

  /// get a number (explicit)
  template <typename T, typename std::enable_if<std::is_arithmetic<T>::value,
                                                int>::type = 0>
  T get_impl(T * /*unused*/) const
  {
    switch (m_type)
    {
      case value_t::number_integer:
      {
        return static_cast<T>(m_value.number_integer);
      }

      case value_t::number_unsigned:
      {
        return static_cast<T>(m_value.number_unsigned);
      }

      case value_t::number_float:
      {
        return static_cast<T>(m_value.number_float);
      }

      default:
      {
        JSON_THROW(
            std::domain_error("type must be number, but is " + type_name()));
      }
    }
  }

  /// get a boolean (explicit)
  boolean_t get_impl(boolean_t * /*unused*/) const
  {
    if (is_boolean())
    {
      return m_value.boolean;
    }
    else
    {
      JSON_THROW(
          std::domain_error("type must be boolean, but is " + type_name()));
    }
  }

  /// get a pointer to the value (object)
  object_t *get_impl_ptr(object_t * /*unused*/) noexcept
  {
    return is_object() ? m_value.object : nullptr;
  }

  /// get a pointer to the value (object)
  constexpr const object_t *get_impl_ptr(const object_t * /*unused*/) const
      noexcept
  {
    return is_object() ? m_value.object : nullptr;
  }

  /// get a pointer to the value (array)
  array_t *get_impl_ptr(array_t * /*unused*/) noexcept
  {
    return is_array() ? m_value.array : nullptr;
  }

  /// get a pointer to the value (array)
  constexpr const array_t *get_impl_ptr(const array_t * /*unused*/) const
      noexcept
  {
    return is_array() ? m_value.array : nullptr;
  }

  /// get a pointer to the value (string)
  string_t *get_impl_ptr(string_t * /*unused*/) noexcept
  {
    return is_string() ? m_value.string : nullptr;
  }

  /// get a pointer to the value (string)
  constexpr const string_t *get_impl_ptr(const string_t * /*unused*/) const
      noexcept
  {
    return is_string() ? m_value.string : nullptr;
  }

  /// get a pointer to the value (boolean)
  boolean_t *get_impl_ptr(boolean_t * /*unused*/) noexcept
  {
    return is_boolean() ? &m_value.boolean : nullptr;
  }

  /// get a pointer to the value (boolean)
  constexpr const boolean_t *get_impl_ptr(const boolean_t * /*unused*/) const
      noexcept
  {
    return is_boolean() ? &m_value.boolean : nullptr;
  }

  /// get a pointer to the value (integer number)
  number_integer_t *get_impl_ptr(number_integer_t * /*unused*/) noexcept
  {
    return is_number_integer() ? &m_value.number_integer : nullptr;
  }

  /// get a pointer to the value (integer number)
  constexpr const number_integer_t *
  get_impl_ptr(const number_integer_t * /*unused*/) const noexcept
  {
    return is_number_integer() ? &m_value.number_integer : nullptr;
  }

  /// get a pointer to the value (unsigned number)
  number_unsigned_t *get_impl_ptr(number_unsigned_t * /*unused*/) noexcept
  {
    return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
  }

  /// get a pointer to the value (unsigned number)
  constexpr const number_unsigned_t *
  get_impl_ptr(const number_unsigned_t * /*unused*/) const noexcept
  {
    return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
  }

  /// get a pointer to the value (floating-point number)
  number_float_t *get_impl_ptr(number_float_t * /*unused*/) noexcept
  {
    return is_number_float() ? &m_value.number_float : nullptr;
  }

  /// get a pointer to the value (floating-point number)
  constexpr const number_float_t *
  get_impl_ptr(const number_float_t * /*unused*/) const noexcept
  {
    return is_number_float() ? &m_value.number_float : nullptr;
  }

  /*!
  @brief helper function to implement get_ref()

  This funcion helps to implement get_ref() without code duplication for
  const and non-const overloads

  @tparam ThisType will be deduced as `basic_json` or `const basic_json`

  @throw std::domain_error if ReferenceType does not match underlying value
  type of the current JSON
  */
  template <typename ReferenceType, typename ThisType>
  static ReferenceType get_ref_impl(ThisType &obj)
  {
    // helper type
    using PointerType = typename std::add_pointer<ReferenceType>::type;

    // delegate the call to get_ptr<>()
    auto ptr = obj.template get_ptr<PointerType>();

    if (ptr != nullptr)
    {
      return *ptr;
    }

    throw std::domain_error(
        "incompatible ReferenceType for get_ref, actual type is " +
        obj.type_name());
  }

public:
  /// @name value access
  /// Direct access to the stored value of a JSON value.
  /// @{

  /*!
  @brief get a value (explicit)

  Explicit type conversion between the JSON value and a compatible value.

  @tparam ValueType non-pointer type compatible to the JSON value, for
  instance `int` for JSON integer numbers, `bool` for JSON booleans, or
  `std::vector` types for JSON arrays

  @return copy of the JSON value, converted to type @a ValueType

  @throw std::domain_error in case passed type @a ValueType is incompatible
  to JSON; example: `"type must be object, but is null"`

  @complexity Linear in the size of the JSON value.

  @liveexample{The example below shows several conversions from JSON values
  to other types. There a few things to note: (1) Floating-point numbers can
  be converted to integers\, (2) A JSON array can be converted to a standard
  `std::vector<short>`\, (3) A JSON object can be converted to C++
  associative containers such as `std::unordered_map<std::string\,
  json>`.,get__ValueType_const}

  @internal
  The idea of using a casted null pointer to choose the correct
  implementation is from <http://stackoverflow.com/a/8315197/266378>.
  @endinternal

  @sa @ref operator ValueType() const for implicit conversion
  @sa @ref get() for pointer-member access

  @since version 1.0.0
  */
  template <typename ValueType,
            typename std::enable_if<not std::is_pointer<ValueType>::value,
                                    int>::type = 0>
  ValueType get() const
  {
    return get_impl(static_cast<ValueType *>(nullptr));
  }

  /*!
  @brief get a pointer value (explicit)

  Explicit pointer access to the internally stored JSON value. No copies are
  made.

  @warning The pointer becomes invalid if the underlying JSON object
  changes.