Skip to content
Snippets Groups Projects
json.hpp 363 KiB
Newer Older
        // newline (0x0a)
        case '\n':
        {
          result[pos + 1] = 'n';
          pos += 2;
          break;
        }
        // carriage return (0x0d)
        case '\r':
        {
          result[pos + 1] = 'r';
          pos += 2;
          break;
        }
        // horizontal tab (0x09)
        case '\t':
        {
          result[pos + 1] = 't';
          pos += 2;
          break;
          if (c >= 0x00 and c <= 0x1f)
          {
            // convert a number 0..15 to its hex representation
            // (0..f)
            static const char hexify[16] = {'0', '1', '2', '3', '4', '5',
                                            '6', '7', '8', '9', 'a', 'b',
                                            'c', 'd', 'e', 'f'};
            // print character c as \uxxxx
            for (const char m :
                 {'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]})
              result[++pos] = m;
            ++pos;
          }
          else
          {
            // all other characters are added as-is
            result[pos++] = c;
          }
          break;
    return result;
  }
  /*!
  @brief internal implementation of the serialization function
  This function is called by the public member function dump and organizes
  the serialization internally. The indentation level is propagated as
  additional parameter. In case of arrays and objects, the function is
  called recursively. Note that
  - strings and object keys are escaped using `escape_string()`
  - integer numbers are converted implicitly via `operator<<`
  - floating-point numbers are converted to a string using `"%g"` format
  @param[out] o              stream to write to
  @param[in] pretty_print    whether the output shall be pretty-printed
  @param[in] indent_step     the indent level
  @param[in] current_indent  the current indent level (only used internally)
  */
  void dump(std::ostream &o, const bool pretty_print,
            const unsigned int indent_step,
            const unsigned int current_indent = 0) const
  {
    // variable to hold indentation for recursive calls
    unsigned int new_indent = current_indent;
    switch (m_type)
      case value_t::object:
      {
        if (m_value.object->empty())
          o << "{}";
          return;
        }
        // increase indentation
        if (pretty_print)
        {
          new_indent += indent_step;
          o << "\n";
        for (auto i = m_value.object->cbegin(); i != m_value.object->cend();
             ++i)
        {
          if (i != m_value.object->cbegin())
          {
            o << (pretty_print ? ",\n" : ",");
          }
          o << string_t(new_indent, ' ') << "\"" << escape_string(i->first)
            << "\":" << (pretty_print ? " " : "");
          i->second.dump(o, pretty_print, indent_step, new_indent);
        }
        // decrease indentation
        if (pretty_print)
        {
          new_indent -= indent_step;
          o << "\n";
        }
        o << string_t(new_indent, ' ') + "}";
        return;
      }
      case value_t::array:
      {
        if (m_value.array->empty())
        {
          o << "[]";
          return;
        }
        // increase indentation
        if (pretty_print)
        {
          new_indent += indent_step;
          o << "\n";
        }
        for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i)
          if (i != m_value.array->cbegin())
          {
            o << (pretty_print ? ",\n" : ",");
          }
          o << string_t(new_indent, ' ');
          i->dump(o, pretty_print, indent_step, new_indent);
        // decrease indentation
        if (pretty_print)
          new_indent -= indent_step;
          o << "\n";
        o << string_t(new_indent, ' ') << "]";
        return;
      }
      case value_t::string:
      {
        o << string_t("\"") << escape_string(*m_value.string) << "\"";
        return;
      }
      case value_t::boolean:
      {
        o << (m_value.boolean ? "true" : "false");
        return;
      }

      case value_t::number_integer:
      {
        o << m_value.number_integer;
        return;
      }

      case value_t::number_unsigned:
      {
        o << m_value.number_unsigned;
        return;
      }

      case value_t::number_float:
      {
        if (m_value.number_float == 0)
          // special case for zero to get "0.0"/"-0.0"
          o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
          o << m_value.number_float;
      case value_t::discarded:
      {
        o << "<discarded>";
        return;
      }
      case value_t::null:
      {
        o << "null";
        return;
      }
private:
  //////////////////////
  // member variables //
  //////////////////////
  /// the type of the current element
  value_t m_type = value_t::null;
  /// the value of the current element
  json_value m_value = {};
private:
  ///////////////
  // iterators //
  ///////////////
  /*!
  @brief an iterator for primitive JSON types
  This class models an iterator for primitive JSON types (boolean, number,
  string). It's only purpose is to allow the iterator/const_iterator classes
  to "iterate" over primitive values. Internally, the iterator is modeled by
  a `difference_type` variable. Value begin_value (`0`) models the begin,
  end_value (`1`) models past the end.
  */
  class primitive_iterator_t
  {
  public:
    /// set iterator to a defined beginning
    void set_begin() noexcept { m_it = begin_value; }
    /// set iterator to a defined past the end
    void set_end() noexcept { m_it = end_value; }
    /// return whether the iterator can be dereferenced
    constexpr bool is_begin() const noexcept { return (m_it == begin_value); }
    /// return whether the iterator is at end
    constexpr bool is_end() const noexcept { return (m_it == end_value); }
    /// return reference to the value to change and compare
    operator difference_type &() noexcept { return m_it; }
    /// return value to compare
    constexpr operator difference_type() const noexcept { return m_it; }
  private:
    static constexpr difference_type begin_value = 0;
    static constexpr difference_type end_value = begin_value + 1;

    /// iterator as signed integer type
    difference_type m_it = std::numeric_limits<std::ptrdiff_t>::denorm_min();
  };

  /*!
  @brief an iterator value

  @note This structure could easily be a union, but MSVC currently does not
  allow unions members with complex constructors, see
  https://github.com/nlohmann/json/pull/105.
  */
  struct internal_iterator
  {
    /// iterator for JSON objects
    typename object_t::iterator object_iterator;
    /// iterator for JSON arrays
    typename array_t::iterator array_iterator;
    /// generic iterator for all other types
    primitive_iterator_t primitive_iterator;

    /// create an uninitialized internal_iterator
    internal_iterator() noexcept : object_iterator(),
                                   array_iterator(),
                                   primitive_iterator()
    {
    }
  };

  /// proxy class for the iterator_wrapper functions
  template <typename IteratorType> class iteration_proxy
  {
  private:
    /// helper class for iteration
    class iteration_proxy_internal
    {
    private:
      /// the iterator
      IteratorType anchor;
      /// an index for arrays (used to create key names)
      size_t array_index = 0;
    public:
      explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it)
      {
      }
      /// dereference operator (needed for range-based for)
      iteration_proxy_internal &operator*() { return *this; }
      /// increment operator (needed for range-based for)
      iteration_proxy_internal &operator++()
      {
        ++anchor;
        ++array_index;
        return *this;
      }

      /// inequality operator (needed for range-based for)
      bool operator!=(const iteration_proxy_internal &o) const
      {
        return anchor != o.anchor;
      }

      /// return key of the iterator
      typename basic_json::string_t key() const
      {
        assert(anchor.m_object != nullptr);

        switch (anchor.m_object->type())
        {
          // use integer array index as key
          case value_t::array:
          {
            return std::to_string(array_index);
          }

          // use key from the object
          case value_t::object:
          {
            return anchor.key();
          }

          // use an empty key for all primitive types
          default:
          {
            return "";
          }
        }
      }

      /// return value of the iterator
      typename IteratorType::reference value() const { return anchor.value(); }
    };
    /// the container to iterate
    typename IteratorType::reference container;
  public:
    /// construct iteration proxy from a container
    explicit iteration_proxy(typename IteratorType::reference cont)
        : container(cont)
    /// return iterator begin (needed for range-based for)
    iteration_proxy_internal begin() noexcept
      return iteration_proxy_internal(container.begin());
    /// return iterator end (needed for range-based for)
    iteration_proxy_internal end() noexcept
      return iteration_proxy_internal(container.end());
public:
  /*!
  @brief a template for a random access iterator for the @ref basic_json class

  This class implements a both iterators (iterator and const_iterator) for the
  @ref basic_json class.
  @note An iterator is called *initialized* when a pointer to a JSON value
        has been set (e.g., by a constructor or a copy assignment). If the
        iterator is default-constructed, it is *uninitialized* and most
        methods are undefined. **The library uses assertions to detect calls
        on uninitialized iterators.**
  @requirement The class satisfies the following concept requirements:
  -
  [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
    The iterator that can be moved to point (forward and backward) to any
    element in constant time.
  @since version 1.0.0, simplified in version 2.0.9
  */
  template <typename U>
  class iter_impl : public std::iterator<std::random_access_iterator_tag, U>
  {
    /// allow basic_json to access private members
    friend class basic_json;
    // make sure U is basic_json or const basic_json
    static_assert(std::is_same<U, basic_json>::value or
                      std::is_same<U, const basic_json>::value,
                  "iter_impl only accepts (const) basic_json");
  public:
    /// the type of the values when the iterator is dereferenced
    using value_type = typename basic_json::value_type;
    /// a type to represent differences between iterators
    using difference_type = typename basic_json::difference_type;
    /// defines a pointer to the type iterated over (value_type)
    using pointer =
        typename std::conditional<std::is_const<U>::value,
                                  typename basic_json::const_pointer,
                                  typename basic_json::pointer>::type;
    /// defines a reference to the type iterated over (value_type)
    using reference =
        typename std::conditional<std::is_const<U>::value,
                                  typename basic_json::const_reference,
                                  typename basic_json::reference>::type;
    /// the category of the iterator
    using iterator_category = std::bidirectional_iterator_tag;
    /// default constructor
    iter_impl() = default;
    /*!
    @brief constructor for a given JSON instance
    @param[in] object  pointer to a JSON object for this iterator
    @pre object != nullptr
    @post The iterator is initialized; i.e. `m_object != nullptr`.
    explicit iter_impl(pointer object) noexcept : m_object(object)
      assert(m_object != nullptr);

      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
          m_it.object_iterator = typename object_t::iterator();
          break;
        case basic_json::value_t::array:
          m_it.array_iterator = typename array_t::iterator();
          break;
        default:
        {
          m_it.primitive_iterator = primitive_iterator_t();
          break;
        }
      }
    /*
    Use operator `const_iterator` instead of `const_iterator(const iterator&
    other) noexcept` to avoid two class definitions for @ref iterator and
    @ref const_iterator.
    This function is only called if this class is an @ref iterator. If this
    class is a @ref const_iterator this function is not called.
    operator const_iterator() const
      const_iterator ret;
      if (m_object)
      {
        ret.m_object = m_object;
        ret.m_it = m_it;
      }
      return ret;
    @brief copy constructor
    @param[in] other  iterator to copy from
    @note It is not checked whether @a other is initialized.
    iter_impl(const iter_impl &other) noexcept : m_object(other.m_object),
                                                 m_it(other.m_it)
    @brief copy assignment
    @param[in,out] other  iterator to copy from
    @note It is not checked whether @a other is initialized.
    */
    iter_impl &operator=(iter_impl other) noexcept(
        std::is_nothrow_move_constructible<pointer>::value
            and std::is_nothrow_move_assignable<pointer>::value and
                std::is_nothrow_move_constructible<internal_iterator>::value and
                    std::is_nothrow_move_assignable<internal_iterator>::value)
    {
      std::swap(m_object, other.m_object);
      std::swap(m_it, other.m_it);
      return *this;
    }
  private:
    /*!
    @brief set the iterator to the first value
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    void set_begin() noexcept
    {
      assert(m_object != nullptr);
      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
        {
          m_it.object_iterator = m_object->m_value.object->begin();
          break;
        }
        case basic_json::value_t::array:
        {
          m_it.array_iterator = m_object->m_value.array->begin();
          break;
        }
        case basic_json::value_t::null:
        {
          // set to end so begin()==end() is true: null is empty
          m_it.primitive_iterator.set_end();
          break;
        }
        default:
        {
          m_it.primitive_iterator.set_begin();
          break;
        }
      }
    }
    /*!
    @brief set the iterator past the last value
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    void set_end() noexcept
      assert(m_object != nullptr);

      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
          m_it.object_iterator = m_object->m_value.object->end();
          break;
        }
        case basic_json::value_t::array:
        {
          m_it.array_iterator = m_object->m_value.array->end();
          break;
        default:
        {
          m_it.primitive_iterator.set_end();
          break;
        }
      }
    @brief return a reference to the value pointed to by the iterator
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    reference operator*() const
      assert(m_object != nullptr);

      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
          assert(m_it.object_iterator != m_object->m_value.object->end());
          return m_it.object_iterator->second;
        case basic_json::value_t::array:
          assert(m_it.array_iterator != m_object->m_value.array->end());
          return *m_it.array_iterator;
        case basic_json::value_t::null:
          JSON_THROW(std::out_of_range("cannot get value"));
          if (m_it.primitive_iterator.is_begin())
          {
            return *m_object;
          }
          JSON_THROW(std::out_of_range("cannot get value"));
        }
      }
    @brief dereference the iterator
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    pointer operator->() const
      assert(m_object != nullptr);

      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
          assert(m_it.object_iterator != m_object->m_value.object->end());
          return &(m_it.object_iterator->second);
        case basic_json::value_t::array:
          assert(m_it.array_iterator != m_object->m_value.array->end());
          return &*m_it.array_iterator;
        default:
        {
          if (m_it.primitive_iterator.is_begin())
          {
            return m_object;
          }

          JSON_THROW(std::out_of_range("cannot get value"));
        }
      }
    @brief post-increment (it++)
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    iter_impl operator++(int)
    {
      auto result = *this;
      ++(*this);
      return result;
    @brief pre-increment (++it)
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    iter_impl &operator++()
    {
      assert(m_object != nullptr);
      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
        {
          std::advance(m_it.object_iterator, 1);
          break;
        }
        case basic_json::value_t::array:
        {
          std::advance(m_it.array_iterator, 1);
          break;
        }
        default:
        {
          ++m_it.primitive_iterator;
          break;
        }
      }
      return *this;
    }
    /*!
    @brief post-decrement (it--)
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    iter_impl operator--(int)
    {
      auto result = *this;
      --(*this);
      return result;
    }
    /*!
    @brief pre-decrement (--it)
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    iter_impl &operator--()
      assert(m_object != nullptr);

      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
          std::advance(m_it.object_iterator, -1);
          break;

        case basic_json::value_t::array:
          std::advance(m_it.array_iterator, -1);
          break;
        default:
        {
          --m_it.primitive_iterator;
          break;
        }
      }
      return *this;
    }
    /*!
    @brief  comparison: equal
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    bool operator==(const iter_impl &other) const
    {
      // if objects are not the same, the comparison is undefined
      if (m_object != other.m_object)
      {
        JSON_THROW(std::domain_error(
            "cannot compare iterators of different containers"));
      }
      assert(m_object != nullptr);
      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
        {
          return (m_it.object_iterator == other.m_it.object_iterator);
        }
        case basic_json::value_t::array:
          return (m_it.array_iterator == other.m_it.array_iterator);
          return (m_it.primitive_iterator == other.m_it.primitive_iterator);
    @brief  comparison: not equal
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    bool operator!=(const iter_impl &other) const
    {
      return not operator==(other);
    }
    /*!
    @brief  comparison: smaller
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    bool operator<(const iter_impl &other) const
    {
      // if objects are not the same, the comparison is undefined
      if (m_object != other.m_object)
      {
        JSON_THROW(std::domain_error(
            "cannot compare iterators of different containers"));
      }
      assert(m_object != nullptr);
      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
        {
          JSON_THROW(
              std::domain_error("cannot compare order of object iterators"));
        }
        case basic_json::value_t::array:
          return (m_it.array_iterator < other.m_it.array_iterator);
          return (m_it.primitive_iterator < other.m_it.primitive_iterator);
    /*!
    @brief  comparison: less than or equal
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    bool operator<=(const iter_impl &other) const
    {
      return not other.operator<(*this);
    }
    /*!
    @brief  comparison: greater than
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    bool operator>(const iter_impl &other) const
    {
      return not operator<=(other);
    }
    /*!
    @brief  comparison: greater than or equal
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    bool operator>=(const iter_impl &other) const
    {
      return not operator<(other);
    }
    @brief  add to iterator
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    iter_impl &operator+=(difference_type i)
    {
      assert(m_object != nullptr);
      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
        {
          JSON_THROW(
              std::domain_error("cannot use offsets with object iterators"));
        }
        case basic_json::value_t::array:
        {
          std::advance(m_it.array_iterator, i);
          break;
        }
          m_it.primitive_iterator += i;
          break;
      return *this;
    @brief  subtract from iterator
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    iter_impl &operator-=(difference_type i) { return operator+=(-i); }
    /*!
    @brief  add to iterator
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    iter_impl operator+(difference_type i)
    {
      auto result = *this;
      result += i;
      return result;
    }
    /*!
    @brief  subtract from iterator
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    iter_impl operator-(difference_type i)
    {
      auto result = *this;
      result -= i;
      return result;
    }
    /*!
    @brief  return difference
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    difference_type operator-(const iter_impl &other) const
      assert(m_object != nullptr);
      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
          JSON_THROW(
              std::domain_error("cannot use offsets with object iterators"));

        case basic_json::value_t::array:
          return m_it.array_iterator - other.m_it.array_iterator;
          return m_it.primitive_iterator - other.m_it.primitive_iterator;
      }
    }

    /*!
    @brief  access to successor
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    reference operator[](difference_type n) const
    {
      assert(m_object != nullptr);

      switch (m_object->m_type)
      {
        case basic_json::value_t::object:
          JSON_THROW(
              std::domain_error("cannot use operator[] for object iterators"));

        case basic_json::value_t::array:
          return *std::next(m_it.array_iterator, n);

        case basic_json::value_t::null:
          JSON_THROW(std::out_of_range("cannot get value"));
          if (m_it.primitive_iterator == -n)
          {
            return *m_object;
          }
          JSON_THROW(std::out_of_range("cannot get value"));
        }
      }
    @brief  return the key of an object iterator
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    typename object_t::key_type key() const
    {
      assert(m_object != nullptr);
      if (m_object->is_object())
      {
        return m_it.object_iterator->first;
      }
      JSON_THROW(
          std::domain_error("cannot use key() for non-object iterators"));
    }
    /*!
    @brief  return the value of an iterator
    @pre The iterator is initialized; i.e. `m_object != nullptr`.
    */
    reference value() const { return operator*(); }
  private:
    /// associated JSON instance
    pointer m_object = nullptr;