// bdljsn_json.h                                                      -*-C++-*-
#ifndef INCLUDED_BDLJSN_JSON
#define INCLUDED_BDLJSN_JSON

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide an in-memory representation of a JSON document.
//
//@CLASSES:
//  bdljsn::Json: representation of a JSON document
//  bdljsn::JsonArray: representation of a JSON object
//  bdljsn::JsonObject: representation of a JSON array
//
//@SEE_ALSO: bdljsn_jsonutil, bdljsn_jsonnumber, bdljsn_jsonnull
//
//@DESCRIPTION: This component provides a value-semantic type, 'bdljsn::Json',
// used as an in-memory representation for a JSON document.  This component
// also provides 'bdljsn::JsonArray' and 'bdljsn::JsonObject' types for
// representing JSON Arrays and Objects respectively.
//
// 'bdljsn::Json' has a close structural similarity to the JSON grammar itself,
// which at a high-level looks like:
//..
//  JSON ::= Object
//         | Array
//         | String
//         | Number
//         | Boolean
//         | null
//..
// Where the Object and Array alternatives can recursively contain JSON.  Just
// like this grammar, a 'bdljsn::Json' is a variant holding either an Object,
// Array, String, Number, Boolean, or null.  These variant selections are
// respresented by the following types:
//
//: o Object: 'JsonObject'
//: o Array: 'JsonArray'
//: o String: 'bsl::string'
//: o Number: 'JsonNumber'
//: o Boolean: 'bool'
//: o null: 'JsonNull'
//
// For more details on the JSON grammar see:
//
//: o website: https://www.json.org/
//: o RFC: https://datatracker.ietf.org/doc/html/rfc8259
//
///Reading and Writing a 'bdljsn::Json' Object
///-------------------------------------------
// 'bdljsn_jsonutil' is the recommended facility to write and read
// 'bdljsn::Json' objects to and from JSON document text.
//
// 'operator<<' overloads are available for all the types.  In addition a
// canonical BDE print method is available for 'bdljsn::Json',
// 'bdljsn::JsonObject', 'bdljsn::JsonArray' 'bdljsn::JsonNumber' and
// 'bdljsn::JsonNull'.
//
///All 'bdljsn::Json' Objects are Valid JSON Documents
///---------------------------------------------------
// Every 'bdljsn::Json' represents a (valid) JSON document ('bdljsn::Json' does
// not have an "invalid" state).  This means writing a 'bdljsn::Json' to a
// string can only fail if an out-of-memory condition occurs.
//
// Similarly, every JSON document has a 'bdljsn::Json' representation.
// However, 'bdljsn::JsonObject' represents Objects having only unique member
// names, meaning that duplicate member names in a JSON document will be
// ignored (see 'bdljsn::JsonUtil' for more information on how duplicate names
// are handled when parsing a JSON document).  Note that the JSON RFC says that
// Object member names "SHOULD be unique", and there is no standard behavior
// for JSON parsers where member names are not unique (typically an in-process
// respresentation with unique member names is used).
//
///Important Preconditions
///- - - - - - - - - - - -
// In order to preserve the invariant that all 'bdljsn::Json' objects are valid
// JSON documents there are some constructors and assignment operations in
// 'bdljsn' package that have notable preconditions:
//
//: o 'bdljsn::JsonNumber' constructors from string require that the string
//:    conform to the JSON grammar for a number (see
//:   'bdljsn::NumberUtil::isValidNumber').
//:
//: o Constructors and assignment operators from 'double' or
//:   'bdldfp::Decimal64' require the value *not* be either INF or NaN
//:
//: o 'bdljsn::Json' constructors and assignment operators require that
//:    strings contain valid UTF-8 (see 'bdlde::Utf8Util::isValid').
//
///'operator==' and the Definition of Value
///----------------------------------------
// 'bdljsn::Json' type's definition of value (i.e., the behavior for
// 'operator==') mirrors comparing the text of two JSON documents where all the
// white-space is ignored.
//
// Concretely, 'bdljsn::Json' is a variant type, whose definition of equality
// is derived from the definition of equality of its constituent types.  I.e.,
// two 'bdljsn::Json' objects compare equal if they have the same 'type' and
// the two objects of that 'type' they contain compare equal.  The definition
// of equality for Object, Array, Boolean, string, and 'JsonNull' types are
// relatively self-explanatory (see respective 'operator==' definitions for
// details).  The definition of equality for 'JsonNumber' is notable:
// 'JsonNumber' objects define value in terms of the text of the JSON number
// string they contain.  So two JSON numbers having the same numerical value
// may compare *unequal* (e.g., "2" and "2.0" and "20e-1" are considered
// different 'JsonNumber' values!).  Note that 'bdljsn::JsonNumber::isEqual'
// provides a semantic comparison of two numbers (see 'bdljsn_jsonnumber' for
// more detail).
//
///Usage
///-----
// This section illustrates the intended use of this component.
//
///Example 1: Constructor a Basic 'bdljsn::Json' Object
/// - - - - - - - - - - - - - - - - - - - - - - - - - -
// Most often 'bdljsn::Json' objects will be written and read from JSON text
// using 'bdljsn_jsonutil'.  In this simple example, we demonstrate manually
// creating the document below and then verify the properties of the resulting
// object:
//..
//  {
//      "number": 3.14,
//      "boolean": true,
//      "string": "text",
//      "null": null,
//      "array": [ "2.76", true ],
//      "object": { "boolean": false }
//  }
//..
// First, we use 'bdljsn::Json::makeObject' to configure the top level
// 'bdljsn::Json' object to be a 'bdljsn::Json' object, and use the various
// manipulators of 'bdljsn::JsonObject' to configure its value:
//..
//  using namespace bdldfp::DecimalLiterals;
//
//  bdljsn::Json json;
//
//  json.makeObject();
//  json["number"]  = 3.14;
//  json["boolean"] = true;
//  json["string"]  = "text";
//  json["array"].makeArray();
//  json["array"].theArray().pushBack(bdljsn::Json(2.76_d64));
//  json["array"].theArray().pushBack(bdljsn::Json(true));
//  json["object"].makeObject()["boolean"] = false;
//..
// Notice that we used 'operator[]' to implicitly create new members of the
// top-level object.  Using 'json.theObject().insert' would be more efficient
// (see example 2).
//
// Finally, we validate the properties of the resulting object:
//..
//  assert(3.14     == json["number"].asDouble());
//  assert(true     == json["boolean"].theBoolean());
//  assert("text"   == json["string"].theString());
//  assert(true     == json["null"].isNull());
//  assert(2.76_d64 == json["array"][0].asDecimal64());
//  assert(false    == json["object"]["boolean"].theBoolean());
//..
//
///Example 2: More Efficiently Creating a 'bdljsn::Json'
///-----------------------------------------------------
// Example 1 used 'operator[]' to implicitly add members to the Objects.  Using
// 'operator[]' is intuitive but not the most efficient method to add new
// members to a 'bdljsn::JsonObject' (similar to using 'operator[]' to add
// elements to an 'unordered_map').  The following code demonstrates a more
// efficient way to create the same 'bdljsn::Json' representation as example 1:
//..
//  using namespace bdldfp::DecimalLiterals;
//
//  bdljsn::Json       json;
//  bdljsn::JsonArray  subArray;
//  bdljsn::JsonObject subObject;
//
//  json.makeObject();
//  json.theObject().insert("number", bdljsn::JsonNumber(3.14));
//  json.theObject().insert("boolean", true);
//  json.theObject().insert("string", "text");
//  json.theObject().insert("null", bdljsn::JsonNull());
//
//  subArray.pushBack(bdljsn::Json(2.76_d64));
//  subArray.pushBack(bdljsn::Json(true));
//  json.theObject().insert("array", bsl::move(subArray));
//
//  subObject.insert("boolean", false);
//  json.theObject().insert("object", bsl::move(subObject));
//..
#include <bdlscm_version.h>

#include <bdlb_variant.h>

#include <bdlde_utf8util.h>

#include <bdljsn_jsonnull.h>
#include <bdljsn_jsonnumber.h>
#include <bdljsn_jsontype.h>

#include <bdlb_transparentequalto.h>
#include <bdlb_transparenthash.h>
#include <bdldfp_decimal.h>

#include <bslma_allocator.h>
#include <bslma_stdallocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_assert.h>
#include <bslmf_enableif.h>
#include <bslmf_issame.h>
#include <bslmf_movableref.h>
#include <bslmf_nestedtraitdeclaration.h>
#include <bslmf_util.h>

#include <bsls_assert.h>
#include <bsls_compilerfeatures.h>
#include <bsls_keyword.h>
#include <bsls_types.h>
#include <bsls_util.h>

#include <bsl_deque.h>
#include <bsl_iostream.h>
#include <bsl_iterator.h>
#include <bsl_unordered_map.h>
#include <bsl_vector.h>

namespace BloombergLP {
namespace bdljsn {

// FORWARD DECLARATIONS
class Json;
class JsonArray;
class JsonObject;

                              // ===============
                              // class JsonArray
                              // ===============

class JsonArray {
    // This type is designed to replicate much of the standard sequence
    // container interface, eliding interfaces that are less relevant for a
    // non-generic container.  Note that a 'bsl::vector' is chosen for the
    // implementation because our implementation permits the element type to be
    // incomplete (when just spelling the type name).

  public:
    // TYPES
    typedef bsl::vector<Json>::iterator       Iterator;
    typedef bsl::vector<Json>::const_iterator ConstIterator;

  private:
    // DATA
    bsl::vector<Json> d_elements;  // the underlying sequence of elements

    // FRIENDS
    friend bool operator==(const JsonArray&, const JsonArray&);
    friend bool operator!=(const JsonArray&, const JsonArray&);
    template <class HASHALG>
    friend void hashAppend(HASHALG&, const JsonArray&);
    friend void swap(JsonArray&, JsonArray&);

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(JsonArray, bslma::UsesBslmaAllocator);
    BSLMF_NESTED_TRAIT_DECLARATION(JsonArray, bslmf::IsBitwiseMoveable);
    BSLMF_NESTED_TRAIT_DECLARATION(JsonArray, bdlb::HasPrintMethod);

    // CREATORS
    JsonArray();
    explicit JsonArray(bslma::Allocator *basicAllocator);
        // Create an empty 'JsonArray'.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is not specified, the
        // currently installed default allocator is used.

    JsonArray(const JsonArray& original, bslma::Allocator *basicAllocator = 0);
        // Create a 'JsonArray' having the same value as the specified
        // 'original' object.  Optionally specify 'basicAllocator' to supply
        // memory.  If 'basicAllocator' is not specified, the allocator
        // associated with 'original' is propagated for use in the
        // newly-created 'JsonArray'.

    JsonArray(bslmf::MovableRef<JsonArray> original) BSLS_KEYWORD_NOEXCEPT;
        // Create a 'JsonArray' having the same value as the specified
        // 'original' object by moving (in constant time) the contents of
        // 'original' to the new 'JsonArray' object.  The allocator associated
        // with 'original' is propagated for use in the newly-created
        // 'JsonArray' object.  'original' is left in a valid but unspecified
        // state.

    JsonArray(bslmf::MovableRef<JsonArray>  original,
              bslma::Allocator             *basicAllocator);
        // Create a 'JsonArray' having the same value as the specified
        // 'original' object that uses the specified 'basicAllocator' to supply
        // memory.  The contents of 'original' are moved (in constant time) to
        // the new 'JsonArray' object if
        // 'basicAllocator == original.allocator()', and are move-inserted (in
        // linear time) using 'basicAllocator' otherwise.  'original' is left
        // in a valid but unspecified state.

    template <class INPUT_ITERATOR>
    JsonArray(INPUT_ITERATOR    first,
              INPUT_ITERATOR    last,
              bslma::Allocator *basicAllocator = 0);
        // Create a 'JsonArray' and insert (in order) each 'Json' object in the
        // range starting at the specified 'first' element, and ending
        // immediately before the specified 'last' element.  Optionally specify
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // specified, the currently installed default allocator is used.  Throw
        // 'bsl::length_error' if the number of elements in '[first .. last)'
        // exceeds the value returned by the method 'maxSize'.  The (template
        // parameter) type 'INPUT_ITERATOR' shall meet the requirements of an
        // input iterator defined in the c++11 standard [24.2.3] providing
        // access to values of a type convertible to 'Json', and 'Json' must be
        // 'emplace-constructible' form '*i', where 'i' is a dereferenceable
        // iterator in the range '[first .. last)'.  The behavior is undefined
        // unless 'first' and 'last' refer to a range of valid values where
        // 'first' is at a position at or before 'last'.

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)

    JsonArray(std::initializer_list<Json>  elements,
    bslma::Allocator                      *basicAllocator = 0);     // IMPLICIT
        // Create a 'JsonArray' and insert (in order) each 'Json' object in the
        // specified 'elements' initializer list.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // specified, the currently installed default allocator is used.

#endif

    // MANIPULATORS
    JsonArray& operator=(const JsonArray& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.  If
        // an exception is thrown, '*this' is left in a valid but unspecified
        // state.

    JsonArray& operator=(bslmf::MovableRef<JsonArray> rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.  The
        // contents of 'rhs' are moved (in constant time) to this JsonArray if
        // 'allocator() == rhs.allocator()'; otherwise, all elements in this
        // 'JsonArray' are either destroyed or move-assigned to and each
        // additional element in 'rhs' is move-inserted into this JsonArray.
        // 'rhs' is left in a valid but unspecified state.

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    JsonArray& operator=(std::initializer_list<Json> initializer);
        // Assign to this object the value resulting from first clearing this
        // JsonArray and then inserting (in order) each 'Json' object in the
        // specified 'initializer' initializer list.  Return a reference to
        // `*this`.  If an exception is thrown, '*this' is left in a valid but
        // unspecified state.

    void assign(std::initializer_list<Json> initializer);
        // Assign to this object the value resulting from first clearing this
        // JsonArray and then inserting (in order) each 'Json' object in the
        // specified 'initializer' initializer list.  If an exception is
        // thrown, '*this' is left in a valid but unspecified state.
#endif

    Json& operator[](bsl::size_t index);
        // Return a reference providing modifiable access to the element at the
        // specified 'index' in this 'JsonArray'.  The behavior is undefined
        // unless 'index < size()'.

    template <class INPUT_ITERATOR>
    void assign(INPUT_ITERATOR first, INPUT_ITERATOR last);
        // Assign to this object the value resulting from first clearing this
        // 'JsonArray' and then inserting (in order) each 'Json' object in the
        // range starting at the specified 'first' element, and ending
        // immediately before the specified 'last' element.  If an exception is
        // thrown, '*this' is left in a valid but unspecified state.  Throw
        // 'bsl::length_error' if 'distance(first,last) > maxSize()'.  The
        // (template parameter) type 'INPUT_ITERATOR' shall meet the
        // requirements of an input iterator defined in the c++11 standard
        // [24.2.3] providing access to values of a type convertible to 'Json',
        // and 'Json' must be 'emplace-constructible' form '*i', where 'i' is a
        // dereferenceable iterator in the range '[first .. last)'.  The
        // behavior is undefined unless 'first' and 'last' refer to a range of
        // valid values where 'first' is at a position at or before 'last'.

    // BDE_VERIFY pragma: -FABC01

    Iterator begin();
        // Return an iterator providing modifiable access to the first element
        // in this 'JsonArray, or the past-the-end iterator if this 'JsonArray'
        // is empty.

    Iterator end();
        // Return the past-the-end iterator providing modifiable access to this
        // 'JsonArray'.

    Json& front();
        // Return a reference providing modifiable access to the first element
        // in this 'JsonArray'.  The behavior is undefined unless this
        // 'JsonArray' is not empty.

    Json& back();
        // Return a reference providing modifiable access to the last element
        // in this 'JsonArray'.  The behavior is undefined unless this
        // 'JsonArray' is not empty.

    // BDE_VERIFY pragma: +FABC01

    void clear();
        // Remove all elements from this 'JsonArray' making its size 0.  Note
        // that although this 'JsonArray' is empty after this method returns,
        // it preserves the same capacity it had before the method was called.

    Iterator erase(bsl::size_t index);
        // Remove from this 'JsonArray' the element at the specified 'index',
        // and return an iterator providing modifiable access to the element
        // immediately following the removed element, or the position returned
        // by the method 'end' if the removed element was the last in the
        // sequence.  The behavior is undefined unless 'index' is in the range
        // '[0 .. size())'.

    Iterator erase(ConstIterator position);
        // Remove from this 'JsonArray' the element at the specified
        // 'position', and return an iterator providing modifiable access to
        // the element immediately following the removed element, or the
        // position returned by the method 'end' if the removed element was the
        // last in the sequence.  The behavior is undefined unless 'position'
        // is an iterator in the range '[cbegin() .. cend())'.

    Iterator erase(ConstIterator first, ConstIterator last);
        // Remove from this 'JsonArray' the sequence of elements starting at
        // the specified 'first' position and ending before the specified
        // 'last' position, and return an iterator providing modifiable access
        // to the element immediately following the last removed element, or
        // the position returned by the method 'end' if the removed elements
        // were last in the sequence.  The behavior is undefined unless 'first'
        // is an iterator in the range '[cbegin() .. cend()]' (both endpoints
        // included) and 'last' is an iterator in the range '[first .. cend()]'
        // (both endpoints included).

    Iterator insert(bsl::size_t index, const Json& json);
        // Insert at the specified 'index' in this JsonArray a copy of the
        // specified 'json', and return an iterator referring to the newly
        // inserted element.  If an exception is thrown, '*this' is unaffected.
        // Throw 'bsl::length_error' if 'size() == maxSize()'.  The behavior is
        // undefined unless 'index' is in the range '[0 .. size()]'.

    Iterator insert(bsl::size_t index, bslmf::MovableRef<Json> json);
        // Insert at the specified 'index' in this JsonArray the specified
        // move-insertable 'json', and return an iterator referring to the
        // newly inserted element.  'json' is left in a valid but unspecified
        // state.  If an exception is thrown, 'this' is unaffected.  Throw
        // 'bsl::length_error' if 'size() == maxSize()'.  The behavior is
        // undefined unless 'index' is in the range '[0 .. size()]' (both
        // endpoints included).

    template <class INPUT_ITERATOR>
    Iterator insert(bsl::size_t    index,
                    INPUT_ITERATOR first,
                    INPUT_ITERATOR last);
        // Insert at the specified 'index' in this JsonArray the values in the
        // range starting at the specified 'first' element, and ending
        // immediately before the specified 'last' element.  Return an iterator
        // referring to the first newly inserted element.  If an exception is
        // thrown, '*this' is unaffected.  Throw 'bsl::length_error' if
        // 'size() + distance(first, last) > maxSize()'.  The (template
        // parameter) type 'INPUT_ITERATOR' shall meet the requirements of an
        // input iterator defined in the C++11 standard [24.2.3] providing
        // access to values of a type convertible to 'Json', and 'Json' must be
        // 'emplace-constructible' from '*i' into this 'JsonArray, where 'i' is
        // a dereferenceable iterator in the range '[first .. last)'.  The
        // behavior is undefined unless 'index' is in the range '[0 .. size()]'
        // (both endpoints included), and 'first' and 'last' refer to a range
        // of valid values where 'first' is at a position at or before 'last'.

    Iterator insert(ConstIterator position, const Json& json);
        // Insert at the specified 'position' in this 'JsonArray' a copy of the
        // specified 'json', and return an iterator referring to the newly
        // inserted element.  If an exception is thrown, '*this' is unaffected.
        // Throw 'bsl::length_error' if 'size() == maxSize()'.  The behavior is
        // undefined unless 'position' is an iterator in the range
        // '[begin() .. end()]' (both endpoints included).

    Iterator insert(ConstIterator position, bslmf::MovableRef<Json> json);
        // Insert at the specified 'position' in this 'JsonArray' the specified
        // move-insertable 'json', and return an iterator referring to the
        // newly inserted element.  'json' is left in a valid but unspecified
        // state.  If an exception is thrown, 'this' is unaffected.  Throw
        // 'bsl::length_error' if 'size() == maxSize()'.  The behavior is
        // undefined unless 'position' is an iterator in the range
        // '[begin() .. end()]' (both endpoints included).

    template <class INPUT_ITERATOR>
    Iterator insert(ConstIterator  position,
                    INPUT_ITERATOR first,
                    INPUT_ITERATOR last);
        // Insert at the specified 'position' in this 'JsonArray' the values in
        // the range starting at the specified 'first' element, and ending
        // immediately before the specified 'last' element.  Return an iterator
        // referring to the first newly inserted element.  If an exception is
        // thrown, '*this' is unaffected.  Throw 'bsl::length_error' if
        // 'size() + distance(first, last) > maxSize()'.  The (template
        // parameter) type 'INPUT_ITERATOR' shall meet the requirements of an
        // input iterator defined in the C++11 standard [24.2.3] providing
        // access to values of a type convertible to 'value_type', and
        // 'value_type' must be 'emplace-constructible' from '*i' into this
        // JsonArray, where 'i' is a dereferenceable iterator in the range
        // '[first .. last)'.  The behavior is undefined unless 'position' is
        // an iterator in the range '[begin() .. end()]' (both endpoints
        // included), and 'first' and 'last' refer to a range of valid values
        // where 'first' is at a position at or before 'last'.

    void popBack();
        // Erase the last element from this 'JsonArray'.  The behavior is
        // undefined if this JsonArray is empty.

    void pushBack(const Json& json);
        // Append to the end of this 'JsonArray' a copy of the specified
        // 'json'.  If an exception is thrown, '*this' is unaffected.  Throw
        // 'bsl::length_error' if 'size() == maxSize()'.

    void pushBack(bslmf::MovableRef<Json> json);
        // Append to the end of this 'JsonArray' the specified move-insertable
        // 'Json'.  'value' is left in a valid but unspecified state.  If an
        // exception is thrown, '*this' is unaffected.  Throw
        // 'bsl::length_error' if 'size() == maxSize()'.

    void resize(bsl::size_t count);
        // Change the size of this 'JsonArray' to the specified 'count'.  If
        // 'count < size()', the elements in the range '[count .. size())' are
        // erased, and this function does not throw.  If 'count > size()', the
        // (newly created) elements in the range '[size() .. count)' are
        // default-constructed 'Json' objects, and if an exception is thrown,
        // '*this' is unaffected.  Throw 'bsl::length_error' if
        // 'count > maxSize()'.

    void resize(bsl::size_t count, const Json& json);
        // Change the size of this JsonArray to the specified 'count',
        // inserting copies of the specified 'json' at the end if
        // 'count > size()'.  If 'count < size()', the elements in the range
        // '[count .. size())' are erased 'json' is ignored, and this method
        // does not throw.  If 'count > size()' and an exception is thrown,
        // '*this' is unaffected.  Throw 'bsl::length_error' if
        // 'count > maxSize()'.

    void swap(JsonArray& other);
        // Exchange the value of this object with that of the specified 'other'
        // object.  If an exception is thrown, both objects are left in valid
        // but unspecified states.  This operation guarantees O[1] complexity.
        // The behavior is undefined unless this object was created with the
        // same allocator as 'other'.

    // ACCESSORS
    const Json& operator[](bsl::size_t index) const;
        // Return a reference providing non-modifiable access to the element at
        // the specified 'index' in this 'JsonArray'.  The behavior is
        // undefined unless 'index < size()'.

    // BDE_VERIFY pragma: -FABC01

    ConstIterator begin() const BSLS_KEYWORD_NOEXCEPT;
    ConstIterator cbegin() const BSLS_KEYWORD_NOEXCEPT;
        // Return an iterator providing non-modifiable access to the first
        // element in this 'JsonArray', and the past-the-end iterator if this
        // 'JsonArray' is empty.

    ConstIterator end() const BSLS_KEYWORD_NOEXCEPT;
    ConstIterator cend() const BSLS_KEYWORD_NOEXCEPT;
        // Return the past-the-end iterator providing non-modifiable access to
        // this 'JsonArray'.

    const Json& front() const;
        // Return a reference providing non-modifiable access to the first
        // element in this 'JsonArray'.  The behavior is undefined unless this
        // 'JsonArray' is not empty.

    const Json& back() const;
        // Return a reference providing non-modifiable access to the last
        // element in this 'JsonArray'.  The behavior is undefined unless this
        // 'JsonArray' is not empty.

    // BDE_VERIFY pragma: +FABC01

    bool empty() const;
        // Return 'true' if this 'JsonArray' has size 0, and 'false' otherwise.

    bsl::size_t size() const;
        // Return the number of elements in this 'JsonArray'.

                                  // Aspects

    bslma::Allocator *allocator() const BSLS_KEYWORD_NOEXCEPT;
        // Return the allocator used by this object to allocate memory.

    bsl::size_t maxSize() const BSLS_KEYWORD_NOEXCEPT;
        // Return a theoretical upper bound on the largest number of elements
        // that this 'JsonArray' could possibly hold.  Note that there is no
        // guarantee that the 'JsonArray' can successfully grow to the returned
        // size, or even close to that size without running out of resources.
        // Also note that requests to create a 'JsonArray' longer than this
        // number of elements are guaranteed to raise a 'bsl::length_error'
        // exception.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
        // Write the value of this object to the specified output 'stream' in a
        // human-readable format, and return a reference to 'stream'.
        // Optionally specify an initial indentation 'level', whose absolute
        // value is incremented recursively for nested objects.  If 'level' is
        // specified, optionally specify 'spacesPerLevel', whose absolute value
        // indicates the number of spaces per indentation level for this and
        // all of its nested objects.  If 'level' is negative, suppress
        // indentation of the first line.  If 'spacesPerLevel' is negative,
        // format the entire output on one line, suppressing all but the
        // initial indentation (as governed by 'level').  If 'stream' is not
        // valid on entry, this operation has no effect.  Note that this
        // human-readable format is not fully specified, and can change without
        // notice.
};

// FREE OPERATORS
bsl::ostream& operator<<(bsl::ostream& stream, const JsonArray& object);
    // Write the value of the specified 'object' to the specified output
    // 'stream' in a single-line format, and return a reference to 'stream'.
    // If 'stream' is not valid on entry, this operation has no effect.  Note
    // that this human-readable format is not fully specified, can change
    // without notice, and is logically equivalent to:
    //..
    //  print(stream, 0, -1);
    //..

bool operator==(const JsonArray& lhs, const JsonArray& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'JsonArray' objects 'lhs' and 'rhs'
    // have the same value if they have the same number of elements, and each
    // element in the ordered sequence of elements of 'lhs' has the same value
    // as the corresponding element in the ordered sequence of elements of
    // 'rhs'.

bool operator!=(const JsonArray& lhs, const JsonArray& rhs);
    // Return 'false' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'true' otherwise.  Two 'JsonArray' objects 'lhs' and 'rhs'
    // have the same value if they have the same number of elements, and each
    // element in the ordered sequence of elements of 'lhs' has the same value
    // as the corresponding element in the ordered sequence of elements of
    // 'rhs'.

template <class HASHALG>
void hashAppend(HASHALG& hashAlg, const JsonArray& object);
    // Invoke the specified 'hashAlg' on the attributes of the specified
    // 'object'.

void swap(JsonArray& a, JsonArray& b);
    // Exchange the value of the specified 'a' 'JsonArray' with that of the
    // specified 'b' 'JsonArray'.  This function provides the no-throw
    // exception-safety guarantee.  This operation has O[1] complexity if 'a'
    // was created with the same allocator as 'b'; otherwise, it has O[n+m]
    // complexity, where n and m are the number of elements in 'a' and 'b',
    // respectively.

                              // ================
                              // class JsonObject
                              // ================

class JsonObject {
    // This type is designed to replicate much of the standard associative
    // container interface, eliding interfaces that are less relevant for a
    // non-generic container, like hasher and comparator access, emplacement,
    // nodes, capacity management, etc.

  private:
    // PRIVATE TYPES
    typedef bsl::unordered_map<bsl::string,
                               Json,
                               bdlb::TransparentHash,
                               bdlb::TransparentEqualTo>
        Container;

  public:
    // TYPES
    typedef Container::value_type     Member;
    typedef Container::const_iterator ConstIterator;
    typedef Container::iterator       Iterator;
    typedef bsl::pair<Iterator, bool> IteratorAndStatus;

  private:
    // DATA
    Container d_members;  // the underlying container of element mappings

    // FRIENDS
    friend bool operator==(const JsonObject&, const JsonObject&);
    friend bool operator!=(const JsonObject&, const JsonObject&);
    template <class HASHALG>
    friend void hashAppend(HASHALG&, const JsonObject&);
    friend void swap(JsonObject&, JsonObject&);

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(JsonObject, bslma::UsesBslmaAllocator);
    BSLMF_NESTED_TRAIT_DECLARATION(JsonObject, bslmf::IsBitwiseMoveable);
    BSLMF_NESTED_TRAIT_DECLARATION(JsonObject, bdlb::HasPrintMethod);

    // CREATORS
    JsonObject();
    explicit JsonObject(bslma::Allocator *basicAllocator);
        // Create an empty 'JsonObject'.  Optionally specify the
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // specified, the currently installed default allocator is used to
        // supply memory.

    JsonObject(const JsonObject&  original,
               bslma::Allocator  *basicAllocator = 0);
        // Create a 'JsonObject' having the same value as the specified
        // 'original'.  Optionally specify the 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is not specified, the currently
        // installed default allocator is used to supply memory.

    JsonObject(bslmf::MovableRef<JsonObject> original) BSLS_KEYWORD_NOEXCEPT;
        // Create a 'JsonObject' having the same value as the specified
        // 'original' object by moving (in constant time) the contents of
        // 'original' to the new 'JsonObject'.  The allocator associated with
        // 'original' is propagated for use in the newly-created 'JsonObject'.
        // 'original' is left in a valid but unspecified state.

    JsonObject(bslmf::MovableRef<JsonObject>  original,
               bslma::Allocator              *basicAllocator);
        // Create a 'JsonObject' having the same value as the specified
        // 'original'.  Use the specified 'basicAllocator' to supply memory.
        // If 'basicAllocator == original.allocator()' the value of 'original'
        // will be moved (in constant time) to the newly-created 'JsonObject',
        // and 'original' will be left in a valid but unspecified state.
        // Otherwise, 'original' is copied, and 'basicAllocator' used to supply
        // memory.

    template <class INPUT_ITERATOR>
    JsonObject(INPUT_ITERATOR    first,
               INPUT_ITERATOR    last,
               bslma::Allocator *basicAllocator = 0);
        // Create an empty 'JsonObject', and then create a 'Json' object for
        // each iterator in the range starting at the specified 'first'
        // iterator and ending immediately before the specified 'last'
        // iterator, by converting from the object referred to by each
        // iterator.  Insert into this 'JsonObject' each such object, ignoring
        // those having a key that appears earlier in the sequence.  Optionally
        // specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is not supplied, the currently installed default
        // allocator is used to supply memory.  The (template parameter) type
        // 'INPUT_ITERATOR' shall meet the requirements of an input iterator
        // defined in the C++11 standard [24.2.3] providing access to values of
        // a type convertible to 'Member'.  The behavior is undefined unless
        // 'first' and 'last' refer to a sequence of valid values where 'first'
        // is at a position at or before 'last', and all keys of all 'Member'
        // objects inserted are valid UTF-8 (see 'bdlde::Utf8Util::isValid').

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    JsonObject(std::initializer_list<Member>  members,
    bslma::Allocator                         *basicAllocator = 0);  // IMPLICIT
        // Create an empty 'JsonObject', and then create a 'Json' object for
        // each in the range specified by 'members' argument, ignoring elements
        // having a key that appears earlier in the sequence.  Optionally
        // specify the 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is not specified, the currently installed default
        // allocator is used to supply memory.  The behavior is undefined
        // unless the keys of all 'Member' objects in 'members' are valid UTF-8
        // (see 'bdlde::Utf8Util::isValid').
#endif

    // MANIPULATORS
    JsonObject& operator=(const JsonObject& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.

    JsonObject& operator=(bslmf::MovableRef<JsonObject> rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.  The
        // contents of 'rhs' are moved (in constant time) to this 'JsonObject'
        // if 'allocator() == rhs.allocator()'; otherwise, all elements in this
        // container are either destroyed or move-assigned to, and each
        // additional element in 'rhs', if any, is move-inserted into this
        // 'JsonObject'.  'rhs' is left in a valid but unspecified state.

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    JsonObject& operator=(std::initializer_list<Member> members);
        // Assign to this object the value resulting from first clearing this
        // 'JsonObject' and then inserting (in order) each 'Member' object in
        // the specified 'members' initializer list.  Return a reference to
        // `*this`.  If an exception is thrown, '*this' is left in a valid but
        // unspecified state.
#endif

    Json& operator[](const bsl::string_view& key);
        // Return a reference providing modifiable access to the 'Json' object
        // associated with the specified 'key' in this 'JsonObject'; if this
        // 'JsonObject' does not already contain a 'Json' object associated
        // with 'key', first insert a new default-constructed 'Json' object
        // associated with 'key'.  The behavior is undefined unless 'key' is
        // valid UTF-8 (see 'bdlde::Utf8Util::isValid').

    // BDE_VERIFY pragma: -FABC01

    Iterator begin() BSLS_KEYWORD_NOEXCEPT;
        // Return an iterator providing modifiable access to the first 'Member'
        // object in the sequence of 'Member' objects maintained by this
        // 'JsonObject', or the 'end' iterator if this 'JsonObject' is empty.

    Iterator end() BSLS_KEYWORD_NOEXCEPT;
        // Return an iterator providing modifiable access to the past-the-end
        // position in the sequence of 'Member' objects maintained by this
        // 'JsonObject'.

    // BDE_VERIFY pragma: +FABC01

    void clear() BSLS_KEYWORD_NOEXCEPT;
        // Remove all entries from this 'JsonObject'.  Note that this
        // 'JsonObject' will be empty after calling this method, but allocated
        // memory may be retained for future use.

    bsl::size_t erase(const bsl::string_view& key);
        // Remove from this 'JsonObject' the 'Member' object having the
        // specified 'key', if it exists, and return 1; otherwise (there is no
        // object with a key equivalent to 'key' in this 'JsonObject') return 0
        // with no other effect.  This method invalidates only iterators and
        // references to the removed element and previously saved values of the
        // 'end()' iterator, and preserves the relative order of the elements
        // not removed.

    Iterator erase(Iterator position);
    Iterator erase(ConstIterator position);
        // Remove from this unordered map the 'value_type' object at the
        // specified 'position', and return an iterator referring to the
        // element immediately following the removed element, or to the
        // past-the-end position if the removed element was the last element in
        // the sequence of elements maintained by this unordered map.  This
        // method invalidates only iterators and references to the removed
        // element and previously saved values of the 'end()' iterator, and
        // preserves the relative order of the elements not removed.  The
        // behavior is undefined unless 'position' refers to a 'value_type'
        // object in this unordered map.

    Iterator find(const bsl::string_view& key);
        // Return an iterator providing modifiable access to the 'Member'
        // object in this 'JsonObject' with a key equivalent to the specified
        // 'key', if such an entry exists, and the past-the-end iterator
        // ('end') otherwise.

    bsl::pair<Iterator, bool> insert(const Member& member);
        // Insert the specified 'member' into this 'JsonObject' if the key (the
        // 'first' element) of the object referred to by 'value' does not
        // already exist in this 'JsonObject'; otherwise, this method has no
        // effect.  Return a 'pair' whose 'first' member is an iterator
        // referring to the (possibley newly inserted) 'value_type' object in
        // this 'JsonObject' whose key is equivalent to that of the object to
        // be inserted, and whose 'second' member is 'true' if a new value was
        // inserted, and 'flase' if a value having an equivalent key was
        // already present.  The behavior is undefined unless 'member.first' is
        // valid UTF-8 (see 'bdlde::Utf8Util::isValid').

    bsl::pair<Iterator, bool> insert(bslmf::MovableRef<Member> member);
        // Insert the specified 'member' into this 'JsonObject' if the key (the
        // 'first' element) of the object referred to by 'member' does not
        // already exist in this 'JsonObject'; otherwise, this method has no
        // effect.  Return a 'pair' whose 'first' member is an iterator
        // referring to the (possibly newly inserted) 'member' object in this
        // 'JsonObject' whose key is the equivalent to that of the object to be
        // inserted, and whose 'second' member is 'true' if a new value was
        // inserted, and 'false' otherwise.  The behavior is undefined unless
        // 'member.first' is valid UTF-8 (see 'bdlde::Utf8Util::isValid').

    template <class INPUT_ITERATOR>
    typename bsl::enable_if<
        bsl::is_convertible<typename bsl::iterator_traits<
                                INPUT_ITERATOR>::value_type::first_type,
                            const bsl::string>::value &&
            bsl::is_convertible<typename bsl::iterator_traits<
                                    INPUT_ITERATOR>::value_type::second_type,
                                bdljsn::Json>::value,
        void>::type
    insert(INPUT_ITERATOR first, INPUT_ITERATOR last);
        // Create a 'member' object for each iterator in the range starting at
        // the specified 'first' iterator and ending immediately before the
        // specified 'last' iterator, by converting from the object referred to
        // by each iterator.  Insert into this 'JsonObject' each such object
        // whose key is not already contained.  The (template parameter) type
        // 'INPUT_ITERATOR' shall meet the requirements of an input iterator
        // defined in the C++11 standard [24.2.3] providing access to values of
        // a type convertible to 'Member'.  The behavior is undefined unless
        // 'first' and 'last' refer to a sequence of value values where 'first'
        // is at a position at or before 'last'.  The behavior is undefined
        // unless the keys of all 'Member' objects inserted are valid UTF-8
        // (see 'bdlde::Utf8Util::isValid').

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    void insert(std::initializer_list<Member> members);
        // Create a 'Member' object for each element in the specified
        // 'members'.  Insert into this 'JsonObject' each such object whose key
        // is not already contained.  The behavior is undefined unless the keys
        // of all 'Member' objects inserted are valid UTF-8 (see
        // 'bdlde::Utf8Util::isValid').
#endif

    template <class VALUE>
    bsl::pair<Iterator, bool> insert(
                               const bsl::string_view&                  key,
                               BSLS_COMPILERFEATURES_FORWARD_REF(VALUE) value);
        // Insert into this 'JsonObject' a 'Member' constructed from the
        // specified 'key' and the specified 'value', respectively, if 'key'
        // does not already exist in this 'JsonObject'; otherwise, this method
        // has no effect.  Return a 'pair' whose 'first' member is an iterator
        // referring to the (possibly newly inserted) 'Member' object in this
        // 'JsonObject' whose key is the equivalent to that of the object to be
        // inserted, and whose 'second' member is 'true' if a new value was
        // inserted, and 'false' otherwise.  The behavior is undefined unless
        // 'key' is valid UTF-8 (see 'bdlde::Utf8Util::isValid').

    void swap(JsonObject& other);
        // Exchange the value of this object with that of the specified 'other'
        // object.  If an exception is thrown, both objects are left in valid
        // but unspecified states.  This operation guarantees O[1] complexity.
        // The behavior is undefined unless this object was created with the
        // same allocator as 'other'.

    // ACCESSORS
    const Json& operator[](const bsl::string_view& key) const;
        // Return a reference providing non-modifiable access to the 'Json'
        // object associated with the specified 'key' in this 'JsonObject'.
        // The behavior is undefined unless 'key' is valid UTF-8 (see
        // 'bdlde::Utf8Util::isValid') and this 'JsonObject' already contains a
        // 'Json' object assicated with 'key'.

    // BDE_VERIFY pragma: -FABC01

    ConstIterator begin() const BSLS_KEYWORD_NOEXCEPT;
    ConstIterator cbegin() const BSLS_KEYWORD_NOEXCEPT;
        // Return an iterator providing non-modifiable access to the first
        // 'Member' object in the sequence of 'Member' objects maintained by
        // this 'JsonObject', or the 'end' iterator if this 'JsonObject' is
        // empty.

    ConstIterator end() const BSLS_KEYWORD_NOEXCEPT;
    ConstIterator cend() const BSLS_KEYWORD_NOEXCEPT;
        // Return an iterator providing non-modifiable access to the
        // past-the-end position in the sequence of 'Member' objects maintained
        // by this 'JsonObject'.

    // BDE_VERIFY pragma: +FABC01

    bool contains(const bsl::string_view& key) const;
        // Return 'true' if there is a 'Member' object in this 'JsonObject'
        // with a key equivalent to the specified 'key', and return 'false'
        // otherwise.

    bool empty() const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if this 'JsonObject' contains no elements, and 'false'
        // otherwise.

    ConstIterator find(const bsl::string_view& key) const;
        // Return an iterator providing non-modifiable access to the 'Member'
        // object in this 'JsonObject' with a key equivalent to the specified
        // 'key', if such an entry exists, and the past-the-end iterator
        // ('end') otherwise.

    bsl::size_t size() const BSLS_KEYWORD_NOEXCEPT;
        // Return the number of elements in this 'JsonObject'.

                                  // Aspects

    bslma::Allocator *allocator() const BSLS_KEYWORD_NOEXCEPT;
        // Return the allocator used by this object to allocate memory.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
        // Write the value of this object to the specified output 'stream' in a
        // human-readable format, and return a reference to 'stream'.
        // Optionally specify an initial indentation 'level', whose absolute
        // value is incremented recursively for nested objects.  If 'level' is
        // specified, optionally specify 'spacesPerLevel', whose absolute value
        // indicates the number of spaces per indentation level for this and
        // all of its nested objects.  If 'level' is negative, suppress
        // indentation of the first line.  If 'spacesPerLevel' is negative,
        // format the entire output on one line, suppressing all but the
        // initial indentation (as governed by 'level').  If 'stream' is not
        // valid on entry, this operation has no effect.  Note that this
        // human-readable format is not fully specified, and can change without
        // notice.
};

// FREE OPERATORS
bsl::ostream& operator<<(bsl::ostream& stream, const JsonObject& object);
    // Write the value of the specified 'object' to the specified output
    // 'stream' in a single-line format, and return a reference to 'stream'.
    // If 'stream' is not valid on entry, this operation has no effect.  Note
    // that this human-readable format is not fully specified, can change
    // without notice, and is logically equivalent to:
    //..
    //  print(stream, 0, -1);
    //..

bool operator==(const JsonObject& lhs, const JsonObject& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'JsonObject' objects have the same
    // value if they have the same number of 'Member' objects, and for each
    // 'Member' object that is contained in 'lhs' there is a key-value pair
    // contained in 'rhs' having the same value, and vice versa.

bool operator!=(const JsonObject& lhs, const JsonObject& rhs);
    // Return 'false' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'true' otherwise.  Two 'JsonObject' objects have the same
    // value if they have the same number of 'Member' objects, and for each
    // 'Member' object that is contained in 'lhs' there is a key-value pair
    // contained in 'rhs' having the same value, and vice versa.

template <class HASHALG>
void hashAppend(HASHALG& hashAlg, const JsonObject& object);
    // Invoke the specified 'hashAlg' on the attributes of the specified
    // 'object'.

void swap(JsonObject& a, JsonObject& b);
    // Exchange the value of the specified 'a' 'JsonObject' with that of the
    // specified 'b' 'JsonObject'.  This function provides the no-throw
    // exception-safety guarantee.  This operation has O[1] complexity if 'a'
    // was created with the same allocator as 'b'; otherwise, it has O[n+m]
    // complexity, where n and m are the number of elements in 'a' and 'b',
    // respectively.

                                 // ==========
                                 // class Json
                                 // ==========

class Json {
    // This type is designed to be a thin wrapper around a variant of the
    // possible JSON types, using a BDE-style variant interface.

    // PRIVATE TYPES
    typedef bdlb::
        Variant<JsonObject, JsonArray, bsl::string, JsonNumber, bool, JsonNull>
            Value;

    BSLMF_ASSERT(0 == JsonType::e_OBJECT);
    BSLMF_ASSERT(1 == JsonType::e_ARRAY);
    BSLMF_ASSERT(2 == JsonType::e_STRING);
    BSLMF_ASSERT(3 == JsonType::e_NUMBER);
    BSLMF_ASSERT(4 == JsonType::e_BOOLEAN);
    BSLMF_ASSERT(5 == JsonType::e_NULL);

  private:
    // DATA
    Value d_value;  // the underlying variant value

    // FRIENDS
    friend bool operator==(const Json&, const Json&);
    friend bool operator!=(const Json&, const Json&);
    template <class HASHALG>
    friend void hashAppend(HASHALG&, const Json&);
    friend void swap(Json&, Json&);

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(Json, bslma::UsesBslmaAllocator);
    BSLMF_NESTED_TRAIT_DECLARATION(Json, bslmf::IsBitwiseMoveable);
    BSLMF_NESTED_TRAIT_DECLARATION(Json, bdlb::HasPrintMethod);

    // CREATORS
    Json();
    explicit Json(bslma::Allocator *basicAllocator);
        // Create a 'Json' object having the type (and the singleton value) of
        // 'JsonNull'.  Optionally specify the 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is not specified, the currently
        // installed default allocator is used to supply memory.

    Json(const Json& original, bslma::Allocator *basicAllocator = 0);
        // Create a 'Json' object having the same value as the specified
        // 'original'.  Optionally specify the 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is not specified, the currently
        // installed default allocator is used to supply memory.

    Json(bslmf::MovableRef<Json> original);
        // Create a 'Json' object having the same value as the specified
        // 'original' object by moving (in constant time) the contents of
        // 'original' to the new 'JsonObject'.  The allocator associated with
        // 'original' is propagated for use in the newly-created 'Json' object.
        // 'original' is left in a valid but unspecified state.

    Json(bslmf::MovableRef<Json> original, bslma::Allocator *basicAllocator);
        // Create a 'Json' object having the same value as the specified
        // 'original'.  Use the specified 'basicAllocator' to supply memory.

    explicit Json(const JsonArray&  array,
                  bslma::Allocator *basicAllocator = 0);
        // Create a 'Json' object having the type 'JsonArray' and the same
        // value as the specified 'array'.  Optionally specify the
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // specified, the currently installed default allocator is used to
        // supply memory.

    explicit Json(bslmf::MovableRef<JsonArray>  array,
                  bslma::Allocator             *basicAllocator = 0);
        // Create a 'Json' object having the type 'JsonArray' and the same
        // value as the specified 'array' object by moving (in constant time)
        // the contents of 'array' to the new 'Json' object.  Optionally
        // specify the 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is not specified, the allocator associated with
        // 'array' is propagated for use in the newly-created 'Json' object.
        // 'array' is left in a valid but unspecified state.

    explicit Json(bool boolean, bslma::Allocator *basicAllocator = 0);
        // Create a 'Json' object having the type 'bool' and the same value as
        // the specified 'boolean'.  Optionally specify the 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is not specified, the
        // currently installed default allocator is used to supply memory.

    explicit Json(const JsonNull& null, bslma::Allocator *basicAllocator = 0);
        // Create a 'Json' object having the type 'JsonNull' and the same value
        // as the specified 'null'.  Optionally specify the 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is not specified, the
        // currently installed default allocator is used to supply memory.

    explicit Json(float number, bslma::Allocator *basicAllocator = 0);
    explicit Json(double number, bslma::Allocator *basicAllocator = 0);
    explicit Json(bdldfp::Decimal64  number,
                  bslma::Allocator  *basicAllocator = 0);
    explicit Json(int number, bslma::Allocator *basicAllocator = 0);
    explicit Json(unsigned int number, bslma::Allocator *basicAllocator = 0);
    explicit Json(bsls::Types::Int64  number,
                  bslma::Allocator   *basicAllocator = 0);
    explicit Json(bsls::Types::Uint64  number,
                  bslma::Allocator    *basicAllocator = 0);
    explicit Json(const JsonNumber&  number,
                  bslma::Allocator  *basicAllocator = 0);
        // Create a 'Json' object having the type 'JsonNumber' and the same
        // value as the specified 'number'.  Optionally specify the
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // specified, the currently installed default allocator is used to
        // supply memory.

    explicit Json(bslmf::MovableRef<JsonNumber>  number,
                  bslma::Allocator              *basicAllocator = 0);
        // Create a 'Json' object having the type 'JsonNumber' and the same
        // value as the specified 'number' object by moving (in constant time)
        // the contents of 'number' to the new 'Json' object.  Optionally
        // specify the 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is not specified, the allocator associated with
        // 'number' is propagated for use in the newly-created 'Json' object.
        // 'number' is left in a valid but unspecified state.

    explicit Json(const JsonObject&  object,
                  bslma::Allocator  *basicAllocator = 0);
        // Create a 'Json' object having the type 'JsonObject' and the same
        // value as the specified 'object'.  Optionally specify the
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // specified, the currently installed default allocator is used to
        // supply memory.

    explicit Json(bslmf::MovableRef<JsonObject>  object,
                  bslma::Allocator              *basicAllocator = 0);
        // Create a 'Json' object having the type 'JsonObject' and the same
        // value as the specified 'object' by moving (in constant time) the
        // contents of 'object' to the new 'Json' object.  Optionally specify
        // the 'basicAllocator' used to supply memory.  If 'basicAllocator' is
        // not specified, the allocator associated with 'object' is propagated
        // for use in the newly-created 'Json' object.  'object' is left in a
        // valid but unspecified state.

    // BDE_VERIFY pragma: -FD06 'string' and 'bsl::string' are too similar
    // BDE_VERIFY pragma: -FD07 'string' and 'bsl::string' are too similar

    explicit Json(const char *string, bslma::Allocator *basicAllocator = 0);
    explicit Json(const bsl::string_view&  string,
                  bslma::Allocator        *basicAllocator = 0);
        // Create a 'Json' object having the type 'bsl::string' and the same
        // value as the specified 'string'.  Optionally specify the
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // specified, the currently installed default allocator is used to
        // supply memory.  The behavior is undefined unless 'string' is valid
        // UTF-8 (see 'bdlde::Utf8Util::isValid').

    // BDE_VERIFY pragma: -IND01 DEDUCE macro confuses bde_verify

    template <class STRING_TYPE>
    explicit Json(
               BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) string,
               bslma::Allocator                    *basicAllocator = 0,
               typename bsl::enable_if<
                   bsl::is_same<STRING_TYPE, bsl::string>::value>::type * = 0);
        // Create a 'Json' object having the type 'bsl::string' and the same
        // value as the specified 'string' by moving (in constant time) the
        // contents of 'string' to the new 'Json' object.  Optionally specify
        // the 'basicAllocator' used to supply memory.  If 'basicAllocator' is
        // not specified, the allocator associated with 'string' is propagated
        // for use in the newly-created 'Json' object.  'string' is left in a
        // valid but unspecified state.  This function does not participate in
        // overload resolution unless the specified 'STRING_TYPE' is
        // 'bsl::string'.  The behavior is undefined unless 'string' is valid
        // UTF-8 (see 'bdlde::Utf8Util::isValid').

    // BDE_VERIFY pragma: +IND01 DEDUCE macro confuses bde_verify
    // BDE_VERIFY pragma: +FD06 'string' and 'bsl::string' are too similar
    // BDE_VERIFY pragma: +FD07 'string' and 'bsl::string' are too similar

    // MANIPULATORS
    Json& operator=(const Json& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.  The
        // value currently held by this variant is destroyed if that value's
        // type is not the same as the type held by the 'rhs' object.

    Json& operator=(bslmf::MovableRef<Json> rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.  The
        // contents of 'rhs' are moved (in constant time) to this 'Json' object
        // if 'allocator() == rhs.allocator()'; otherwise they are copied.
        // 'rhs' is left in a valid but unspecified state.

    Json& operator=(float rhs);
    Json& operator=(double rhs);
    Json& operator=(bdldfp::Decimal64 rhs);
    Json& operator=(int rhs);
    Json& operator=(unsigned int rhs);
    Json& operator=(bsls::Types::Int64 rhs);
    Json& operator=(bsls::Types::Uint64 rhs);
    Json& operator=(const JsonNumber& rhs);
        // Assign to this object a value of type 'JsonNumber' initialized from
        // the specified 'rhs', and return a reference providing modifiable
        // access to this object.  The value currently held by this object (if
        // any) is destroyed if that value's type is not 'JsonNumber'.

    Json& operator=(bslmf::MovableRef<JsonNumber> rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.  The
        // contents of 'rhs' are moved (in constant time) to this 'Json' object
        // if 'allocator() == rhs.allocator()'; otherwise, they are copied.
        // 'rhs' is left in a valid but unspecified state.  The value currently
        // held by this object (if any) is destroyed if that value's type is
        // not 'JsonNumber'.

    Json& operator=(const char *rhs);
    Json& operator=(const bsl::string_view& rhs);
        // Assign to this object a value of type 'bsl::string' initialized from
        // the specified 'rhs', and return a reference providing modifiable
        // access to this object.  The value currently held by this object (if
        // any) is destroyed if that value's type is not 'bsl::string'.  The
        // behavior is undefined unless 'rhs' is valid UTF-8 (see
        // 'bdlde::Utf8Util::isValid').

    template <class STRING_TYPE>
    typename bsl::enable_if<bsl::is_same<STRING_TYPE, bsl::string>::value,
                            Json>::type&
    operator=(BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.  The
        // contents of 'rhs' are moved (in constant time) to this 'Json' object
        // if 'allocator() == rhs.allocator()'; otherwise, they are copied.
        // 'rhs' is left in a valid but unspecified state.  The value currently
        // held by this object (if any) is destroyed if that value's type is
        // not 'bsl::string'.  This function does not participate in overload
        // resolution unless the specified 'STRING_TYPE' is 'bsl::string'.  The
        // behavior is undefined unless 'rhs' is valid UTF-8 (see
        // 'bdlde::Utf8Util::isValid').

    Json& operator=(bool rhs);
        // Assign to this object a value of type 'bool' initialized from the
        // specified 'rhs', and return a reference providing modifiable access
        // to this object.  The value currently held by this object (if any) is
        // destroyed if that value's type is not 'bsl::string'.

    Json& operator=(const JsonObject& rhs);
        // Assign to this object a value of type 'JsonObject' initialized from
        // the specified 'rhs', and return a reference providing modifiable
        // access to this object.  The value currently held by this object (if
        // any) is destroyed if that value's type is not 'JsonObject'.

    Json& operator=(bslmf::MovableRef<JsonObject> rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.  The
        // contents of 'rhs' are moved (in constant time) to this 'Json' object
        // if 'allocator() == rhs.allocator()'; otherwise, they are copied.
        // 'rhs' is left in a valid but unspecified state.  The value currently
        // held by this object (if any) is destroyed if that value's type is
        // not 'JsonObject'.

    Json& operator=(const JsonArray& rhs);
        // Assign to this object a value of type 'JsonArray' initialized from
        // the specified 'rhs', and return a reference providing modifiable
        // access to this object.  The value currently held by this object (if
        // any) is destroyed if that value's type is not 'JsonArray'.

    Json& operator=(bslmf::MovableRef<JsonArray> rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.  The
        // contents of 'rhs' are moved (in constant time) to this 'Json' object
        // if 'allocator() == rhs.allocator()'; otherwise, they are copied.
        // 'rhs' is left in a valid but unspecified state.  The value currently
        // held by this object (if any) is destroyed if that value's type is
        // not 'JsonArray'.

    Json& operator=(const JsonNull&);
        // Assign to this object the singleton value of type 'JsonNull', and
        // return a reference providing modifiable access to this object.  The
        // value currently held by this object (if any) is destroyed if that
        // value's type is not 'JsonNull'.

    Json& operator=(bslmf::MovableRef<JsonNull>);
        // Assign to this object the singleton value of type 'JsonNull', and
        // return a reference providing modifiable access to this object.
        // 'rhs' is left in a valid but unspecified state.  The value currently
        // held by this object (if any) is destroyed if that value's type is
        // not 'JsonNull'.

    JsonArray& makeArray();
    JsonArray& makeArray(const JsonArray& array);
    JsonArray& makeArray(bslmf::MovableRef<JsonArray> array);
        // Create an instance of type 'JsonArray' in this object, using the
        // allocator currently held by this object to supply memory, and return
        // a reference providing modifiable access to the created instance.
        // Optionally specify 'array' to initialize the 'JsonArray' created.
        // This method first destroys the current value held by this object
        // (even if the type currently held is 'JsonArray').

    bool& makeBoolean();
    bool& makeBoolean(bool boolean);
        // Create an instance of type 'bool' in this object and return a
        // reference providing modifiable access to the created instance.
        // Optionally specify 'boolean' to initialize the 'bool' created.  This
        // method first destroys the current value held by this object.

    void makeNull();
        // Create an instance of type 'JsonNull' in this object.  This method
        // first destroys the current value held by this object.

    JsonNumber& makeNumber();
    JsonNumber& makeNumber(const JsonNumber& number);
    JsonNumber& makeNumber(bslmf::MovableRef<JsonNumber> number);
        // Create an instance of type 'JsonNumber' in this object, using the
        // allocator currently held by this object to supply memory, and return
        // a reference providing modifiable access to the created instance.
        // Optionally specify 'number' to initialize the 'JsonNumber' created.
        // This method first destroys the current value held by this object
        // (even if the type currently held is 'JsonNumber').

    JsonObject& makeObject();
    JsonObject& makeObject(const JsonObject& object);
    JsonObject& makeObject(bslmf::MovableRef<JsonObject> object);
        // Create an instance of type 'JsonObject' in this object, using the
        // allocator currently held by this object to supply memory, and return
        // a reference providing modifiable access to the created instance.
        // Optionally specify 'object' to initialize the 'JsonObject' created.
        // This method first destroys the current value held by this object
        // (even if the type currently held is 'JsonObject').

    // BDE_VERIFY pragma: -FD06 'string' and 'bsl::string' are too similar
    // BDE_VERIFY pragma: -FD07 'string' and 'bsl::string' are too similar

    void makeString(const char *string);
    void makeString(const bsl::string_view& string);
        // Create an instance of type 'bsl::string' in this object, using the
        // allocator currently held by this object to supply memory.
        // Optionally specify 'string' to initialize the 'bsl::string' created.
        // This method first destroys the current value held by this object
        // (even if the type currently held is 'bsl::string').  The behavior is
        // undefined unless 'string' is valid UTF-8 (see
        // 'bdlde::Utf8Util::isValid').

    template <class STRING_TYPE>
    typename bsl::enable_if<
        bsl::is_same<STRING_TYPE, bsl::string>::value>::type
        makeString(BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) string);
        // Create an instance of type 'bsl::string' in this object, using the
        // allocator currently held by this object to supply memory.
        // Optionally specify 'string' to initialize the 'bsl::string' created.
        // This method first destroys the current value held by this object
        // (even if the type currently held is 'bsl::string').  This function
        // does not participate in overload resolution unless the specified
        // 'STRING_TYPE' is 'bsl::string'.  The behavior is undefined unless
        // 'string' is valid UTF-8 (see 'bdlde::Utf8Util::isValid').

    // BDE_VERIFY pragma: +FD06 'string' and 'bsl::string' are too similar
    // BDE_VERIFY pragma: +FD07 'string' and 'bsl::string' are too similar

    void swap(Json& other);
        // Exchange the value of this with that of the specified 'other'.  If
        // an exception is thrown, both objects are left in valid but
        // unspecified states.  This operation guarantees O[1] complexity.  The
        // behavior is undefined unless this object was created with the same
        // allocator as 'other'.

    JsonArray& theArray();
        // Return a reference providing modifiable access to the value of type
        // 'JsonArray' held by this object.  The behavior is undefined unless
        // 'isArray()' returns true.

    bool& theBoolean();
        // Return a reference providing modifiable access to the value of type
        // 'boolean' held by this object.  The behavior is undefined unless
        // 'isBoolean()' returns true.

    JsonNull& theNull();
        // Return a reference providing modifiable access to the value of type
        // 'JsonNull' held by this object.  The behavior is undefined unless
        // 'isNull()' returns true.

    JsonNumber& theNumber();
        // Return a reference providing modifiable access to the value of type
        // 'JsonNumber' held by this object.  The behavior is undefined unless
        // 'isNumber()' returns true.

    JsonObject& theObject();
        // Return a reference providing modifiable access to the value of type
        // 'JsonObject' held by this object.  The behavior is undefined unless
        // 'isObject()' returns true.

#if defined(BSLS_COMPILERFEATURES_SUPPORT_OPERATOR_EXPLICIT)
    explicit operator JsonArray &();
        // Return a reference providing modifiable access to the value of type
        // 'JsonArray' held by this object.  The behavior is undefined unless
        // 'isArray()' returns true.

    explicit operator bool &();
        // Return a reference providing modifiable access to the value of type
        // 'boolean' held by this object.  The behavior is undefined unless
        // 'isBoolean()' returns true.

    explicit operator JsonNull &();
        // Return a reference providing modifiable access to the value of type
        // 'JsonNull' held by this object.  The behavior is undefined unless
        // 'isNull()' returns true.

    explicit operator JsonNumber &();
        // Return a reference providing modifiable access to the value of type
        // 'JsonNumber' held by this object.  The behavior is undefined unless
        // 'isNumber()' returns true.

    explicit operator JsonObject &();
        // Return a reference providing modifiable access to the value of type
        // 'JsonObject' held by this object.  The behavior is undefined unless
        // 'isObject()' returns true.
#endif

    Json& operator[](const bsl::string_view& key);
        // Return a reference providing modifiable access to the 'Json' object
        // assicated with the specified 'key' in the 'JsonObject' held by this
        // object; if the 'JsonObject' does not already contain a 'Json' object
        // assicated with 'key', first insert a new default-constructed 'Json'
        // object assicated with 'key'.  The behavior is undefined unless
        // 'isObject()' returns true.

    Json& operator[](bsl::size_t index);
        // Return a reference providing modifiable access to the element at the
        // specified 'index' in the 'JsonArray' held by this object.  The
        // behavior is undefined unless 'isArray()' returns true and
        // 'index < theArray().size()'.

    // ACCESSORS
    // BDE_VERIFY pragma: -FABC01
    int asInt(int *result) const;
    int asInt64(bsls::Types::Int64 *result) const;
    int asUint(unsigned int *result) const;
    int asUint64(bsls::Types::Uint64 *result) const;
        // Load into the specified 'result' the integer value of the value of
        // type 'JsonNumber' held by this object.  Return 0 on success,
        // 'JsonNumber::k_OVERFLOW' if 'value' is larger than can be
        // represented by 'result', 'JsonNumber::k_UNDERFLOW' if 'value' is
        // smaller than can be represented by 'result', and
        // 'JsonNumber::k_NOT_INTEGRAL' if 'value' is not an integral number
        // (i.e., there is a fractional part).  For underflow, 'result' will be
        // loaded with the minimum representable value, for overflow, 'result'
        // will be loaded with the maximum representable value, for
        // non-integral values 'result' will be loaded with the integer part of
        // 'value' (trucating the value to the nearest integer).  If the result
        // is not an integer and also either overflows or underflows, it is
        // treated as an overflow or underflow (respectively).  Note that this
        // operation returns a status value (unlike similar floating point
        // conversions) because typically it is an error if a conversion to an
        // integer results in an inexact value.  The behavior is undefined
        // unless 'isNumber()' returns true.

    float             asFloat() const;
    double            asDouble() const;
    bdldfp::Decimal64 asDecimal64() const;
        // Return the closest floating point representation to the value of the
        // type 'JsonNumber' held by this object.  If this number is outside
        // the representable range, return +INF or -INF (as appropriate).  The
        // behavior is undefined unless 'isNumber()' returns true.
    // BDE_VERIFY pragma: +FABC01

    int asDecimal64Exact(bdldfp::Decimal64 *result) const;
        // Load the specified 'result' with the closest floating point
        // representation to the value of type 'JsonNumber' held by this
        // object, even if a non-zero status is returned.  Return 0 if this
        // number can be represented exactly, and return
        // 'JsonNumber::k_INEXACT' if 'value' cannot be represented exactly.
        // If this number is outside the representable range, load 'result'
        // with +INF or -INF (as appropriate).  A number can be represented
        // exactly as a 'Decimal64' if, for the significand and exponent,
        // 'abs(significand) <= 9,999,999,999,999,999' and '-398 <= exponent <=
        // 369'.  The behavior is undefined unless 'isNumber()' returns true;

    bool isArray() const;
        // Return true if the value held by this object is of type 'JsonArray',
        // and false otherwise.

    bool isBoolean() const;
        // Return true if the value held by this object is of type 'bool', and
        // false otherwise.

    bool isNull() const;
        // Return true if the value held by this object is of type 'JsonNull',
        // and false otherwise.

    bool isNumber() const;
        // Return true if the value held by this object is of type
        // 'JsonNumber', and false otherwise.

    bool isObject() const;
        // Return true if the value held by this object is of type
        // 'JsonObject', and false otherwise.

    bool isString() const;
        // Return true if the value held by this object is of type
        // 'bsl::string', and false otherwise.

    const JsonArray& theArray() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'JsonArray' held by this object.  The behavior is undefined
        // unless 'isArray()' returns true.

    const bool& theBoolean() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'boolean' held by this object.  The behavior is undefined
        // unless 'isBoolean()' returns true.

    const JsonNull& theNull() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'JsonNull' held by this object.  The behavior is undefined
        // unless 'isNull()' returns true.

    const JsonNumber& theNumber() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'JsonNumber' held by this object.  The behavior is undefined
        // unless 'isNumber()' returns true.

    const JsonObject& theObject() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'JsonObject' held by this object.  The behavior is undefined
        // unless 'isObject()' returns true.

    const bsl::string& theString() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'JsonString' held by this object.  The behavior is undefined
        // unless 'isString()' returns true.

    JsonType::Enum type() const BSLS_KEYWORD_NOEXCEPT;
        // Return the type of this 'Json' value.

    const Json& operator[](const bsl::string_view& key) const;
        // Return a reference providing non-modifiable access to the 'Json'
        // object associated with the specified 'key' in this 'JsonObject'.
        // The behavior is undefined unless 'key' is valid UTF-8 (see
        // 'bdlde::Utf8Util::isValid') and this 'JsonObject' already contains a
        // 'Json' object assicated with 'key'.

    const Json& operator[](bsl::size_t index) const;
        // Return a reference providing non-modifiable access to the element at
        // the specified 'index' in the 'JsonArray' held by this object.  The
        // behavior is undefined unless 'isArray()' returns true and
        // 'index < theArray().size()'.

#if defined(BSLS_COMPILERFEATURES_SUPPORT_OPERATOR_EXPLICIT)
    explicit operator const JsonArray &() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'JsonArray' held by this object.  The behavior is undefined
        // unless 'isArray()' returns true.

    explicit operator const bool &() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'boolean' held by this object.  The behavior is undefined
        // unless 'isBoolean()' returns true.

    explicit operator const JsonNull &() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'JsonNull' held by this object.  The behavior is undefined
        // unless 'isNull()' returns true.

    explicit operator const JsonNumber &() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'JsonNumber' held by this object.  The behavior is undefined
        // unless 'isNumber()' returns true.

    explicit operator const JsonObject &() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'JsonObject' held by this object.  The behavior is undefined
        // unless 'isObject()' returns true.

    explicit operator const bsl::string &() const;
        // Return a reference providing non-modifiable access to the value of
        // type 'JsonString' held by this object.  The behavior is undefined
        // unless 'isString()' returns true.
#endif

    bsl::size_t size() const;
        // Return the number of elements in the 'JsonObject' or 'JsonArray'
        // held by this object.  The behavior is undefined unless
        // 'isArray() || isObject()' evaluates to true.

                                  // Aspects

    bslma::Allocator *allocator() const BSLS_KEYWORD_NOEXCEPT;
        // Return the allocator used by this object to allocate memory.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
        // Write the value of this object to the specified output 'stream' in a
        // human-readable format, and return a reference to 'stream'.
        // Optionally specify an initial indentation 'level', whose absolute
        // value is incremented recursively for nested objects.  If 'level' is
        // specified, optionally specify 'spacesPerLevel', whose absolute value
        // indicates the number of spaces per indentation level for this and
        // all of its nested objects.  If 'level' is negative, suppress
        // indentation of the first line.  If 'spacesPerLevel' is negative,
        // format the entire output on one line, suppressing all but the
        // initial indentation (as governed by 'level').  If 'stream' is not
        // valid on entry, this operation has no effect.  Note that this
        // human-readable format is not fully specified, and can change without
        // notice.
};

// FREE OPERATORS
bsl::ostream& operator<<(bsl::ostream& stream, const Json& object);
    // Write the value of the specified 'object' to the specified output
    // 'stream' in a single-line format, and return a reference to 'stream'.
    // If 'stream' is not valid on entry, this operation has no effect.  Note
    // that this human-readable format is not fully specified, can change
    // without notice, and is logically equivalent to:
    //..
    //  print(stream, 0, -1);
    //..

bool operator==(const Json& lhs, const Json& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'Json' objects 'lhs' and 'rhs' have
    // the same value if they hold objects of the same type, and those objects
    // have the same value.

bool operator!=(const Json& lhs, const Json& rhs);
    // Return 'false' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'true' otherwise.  Two 'Json' objects 'lhs' and 'rhs' have
    // the same value if they hold objects of the same type, and those objects
    // have the same value.

template <class HASHALG>
void hashAppend(HASHALG& hashAlg, const Json& object);
    // Invoke the specified 'hashAlg' on the attributes of the specified
    // 'object'.

void swap(Json& a, Json& b);
    // Exchange the value of the specified 'a' 'Json' object with that of the
    // specified 'b' 'Json' object.  If an exception is thrown, both objects
    // are left in valid but unspecified states.  This operation guarantees
    // O[1] complexity.  The behavior is undefined unless 'a' was created with
    // the same allocator as 'b'.

// ============================================================================
//                            INLINE DEFINITIONS
// ============================================================================

                              // ---------------
                              // class JsonArray
                              // ---------------

// CREATORS
inline
JsonArray::JsonArray()
: d_elements()
{
}

inline
JsonArray::JsonArray(bslma::Allocator *basicAllocator)
: d_elements(basicAllocator)
{
}

inline
JsonArray::JsonArray(const JsonArray&  original,
                     bslma::Allocator *basicAllocator)
: d_elements(original.d_elements, basicAllocator)
{
}

inline
JsonArray::JsonArray(bslmf::MovableRef<JsonArray> original)
    BSLS_KEYWORD_NOEXCEPT
: d_elements(bslmf::MovableRefUtil::move(
                           bslmf::MovableRefUtil::access(original).d_elements))
{
}

inline
JsonArray::JsonArray(bslmf::MovableRef<JsonArray>  original,
                     bslma::Allocator             *basicAllocator)
: d_elements(basicAllocator)
{
    JsonArray& originalAsLvalue = bslmf::MovableRefUtil::access(original);

    if (basicAllocator == originalAsLvalue.allocator()) {
        d_elements = bslmf::MovableRefUtil::move(originalAsLvalue.d_elements);
    }
    else {
        d_elements = originalAsLvalue.d_elements;
    }
}

template <class INPUT_ITERATOR>
inline
JsonArray::JsonArray(INPUT_ITERATOR    first,
                     INPUT_ITERATOR    last,
                     bslma::Allocator *basicAllocator)
: d_elements(first, last, basicAllocator)
{
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
inline
JsonArray::JsonArray(std::initializer_list<Json>  elements,
                     bslma::Allocator            *basicAllocator)
: d_elements(elements, basicAllocator)
{
}
#endif

// MANIPULATORS
inline
JsonArray& JsonArray::operator=(const JsonArray& rhs)
{
    d_elements = rhs.d_elements;
    return *this;
}

inline
JsonArray& JsonArray::operator=(bslmf::MovableRef<JsonArray> rhs)
{
    d_elements = bslmf::MovableRefUtil::move(
                                bslmf::MovableRefUtil::access(rhs).d_elements);
    return *this;
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
inline
JsonArray& JsonArray::operator=(std::initializer_list<Json> initializer)
{
    d_elements = initializer;
    return *this;
}
#endif

inline
Json& JsonArray::operator[](bsl::size_t index)
{
    BSLS_ASSERT(index < d_elements.size());
    return d_elements[index];
}

template <class INPUT_ITERATOR>
inline
void JsonArray::assign(INPUT_ITERATOR first, INPUT_ITERATOR last)
{
    d_elements.assign(first, last);
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
inline
void JsonArray::assign(std::initializer_list<Json> initializer)
{
    d_elements.assign(initializer);
}
#endif

inline
Json& JsonArray::back()
{
    return d_elements.back();
}

inline
JsonArray::Iterator JsonArray::begin()
{
    return d_elements.begin();
}

inline
void JsonArray::clear()
{
    d_elements.clear();
}

inline
JsonArray::Iterator JsonArray::end()
{
    return d_elements.end();
}

inline
JsonArray::Iterator JsonArray::erase(bsl::size_t index)
{
    return d_elements.erase(d_elements.begin() + index);
}

inline
JsonArray::Iterator JsonArray::erase(ConstIterator position)
{
    return d_elements.erase(position);
}

inline
JsonArray::Iterator JsonArray::erase(ConstIterator first, ConstIterator last)
{
    return d_elements.erase(first, last);
}

inline
Json& JsonArray::front()
{
    return d_elements.front();
}

inline
JsonArray::Iterator JsonArray::insert(bsl::size_t index, const Json& json)
{
    return d_elements.insert(d_elements.begin() + index, json);
}

inline
JsonArray::Iterator JsonArray::insert(bsl::size_t             index,
                                      bslmf::MovableRef<Json> json)
{
    return d_elements.insert(d_elements.begin() + index,
                             bslmf::MovableRefUtil::move(json));
}

template <class INPUT_ITERATOR>
inline
JsonArray::Iterator JsonArray::insert(bsl::size_t    index,
                                      INPUT_ITERATOR first,
                                      INPUT_ITERATOR last)
{
    return d_elements.insert(d_elements.begin() + index, first, last);
}

inline
JsonArray::Iterator JsonArray::insert(ConstIterator position, const Json& json)
{
    return d_elements.insert(position, json);
}

inline
JsonArray::Iterator JsonArray::insert(ConstIterator           position,
                                      bslmf::MovableRef<Json> json)
{
    return d_elements.insert(position, bslmf::MovableRefUtil::move(json));
}

template <class INPUT_ITERATOR>
inline
JsonArray::Iterator JsonArray::insert(ConstIterator  position,
                                      INPUT_ITERATOR first,
                                      INPUT_ITERATOR last)
{
    return d_elements.insert(position, first, last);
}

inline
void JsonArray::popBack()
{
    d_elements.pop_back();
}

inline
void JsonArray::pushBack(const Json& json)
{
    d_elements.push_back(json);
}

inline
void JsonArray::pushBack(bslmf::MovableRef<Json> json)
{
    d_elements.push_back(bslmf::MovableRefUtil::move(json));
}

inline
void JsonArray::resize(bsl::size_t count)
{
    d_elements.resize(count);
}

inline
void JsonArray::resize(bsl::size_t count, const Json& json)
{
    d_elements.resize(count, json);
}

inline
void JsonArray::swap(JsonArray& other)
{
    BSLS_ASSERT(allocator() == other.allocator());
    d_elements.swap(other.d_elements);
}

// ACCESSORS
inline
const Json& JsonArray::operator[](bsl::size_t index) const
{
    BSLS_ASSERT(index < d_elements.size());
    return d_elements[index];
}

inline
bslma::Allocator *JsonArray::allocator() const BSLS_KEYWORD_NOEXCEPT
{
    return d_elements.get_allocator().mechanism();
}

inline
const Json& JsonArray::back() const
{
    return d_elements.back();
}

inline
JsonArray::ConstIterator JsonArray::begin() const BSLS_KEYWORD_NOEXCEPT
{
    return d_elements.begin();
}

inline
JsonArray::ConstIterator JsonArray::cbegin() const BSLS_KEYWORD_NOEXCEPT
{
    return d_elements.begin();
}

inline
JsonArray::ConstIterator JsonArray::cend() const BSLS_KEYWORD_NOEXCEPT
{
    return d_elements.end();
}

inline
bool JsonArray::empty() const
{
    return d_elements.empty();
}

inline
JsonArray::ConstIterator JsonArray::end() const BSLS_KEYWORD_NOEXCEPT
{
    return d_elements.end();
}

inline
const Json& JsonArray::front() const
{
    return d_elements.front();
}

inline
bsl::size_t JsonArray::maxSize() const BSLS_KEYWORD_NOEXCEPT
{
    return d_elements.max_size();
}

inline
bsl::size_t JsonArray::size() const
{
    return d_elements.size();
}

                              // ----------------
                              // class JsonObject
                              // ----------------

// CREATORS
inline
JsonObject::JsonObject()
: d_members()
{
}

inline
JsonObject::JsonObject(bslma::Allocator *basicAllocator)
: d_members(basicAllocator)
{
}

inline
JsonObject::JsonObject(const JsonObject&  original,
                       bslma::Allocator  *basicAllocator)
: d_members(original.d_members, basicAllocator)
{
}

inline
JsonObject::JsonObject(bslmf::MovableRef<JsonObject> original)
    BSLS_KEYWORD_NOEXCEPT
: d_members(bslmf::MovableRefUtil::move(
                            bslmf::MovableRefUtil::access(original).d_members))
{
}

inline
JsonObject::JsonObject(bslmf::MovableRef<JsonObject>  original,
                       bslma::Allocator              *basicAllocator)
: d_members(basicAllocator)
{
    JsonObject& originalAsLvalue = bslmf::MovableRefUtil::access(original);

    if (basicAllocator == originalAsLvalue.allocator()) {
        d_members = bslmf::MovableRefUtil::move(originalAsLvalue.d_members);
    }
    else {
        d_members = originalAsLvalue.d_members;
    }
}

template <class INPUT_ITERATOR>
inline
JsonObject::JsonObject(INPUT_ITERATOR    first,
                       INPUT_ITERATOR    last,
                       bslma::Allocator *basicAllocator)
: d_members(first, last, basicAllocator)
{
#ifdef BSLS_ASSERT_IS_ACTIVE
    for (Container::const_iterator iter = d_members.begin();
         iter != d_members.end();
         ++iter) {
        BSLS_ASSERT(bdlde::Utf8Util::isValid(iter->first.data(),
                                             iter->first.size()));
    }
#endif
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
inline
JsonObject::JsonObject(std::initializer_list<Member>  members,
                       bslma::Allocator              *basicAllocator)
: d_members(members, basicAllocator)
{
#ifdef BSLS_ASSERT_IS_ACTIVE
    for (Container::const_iterator iter = d_members.begin();
         iter != d_members.end();
         ++iter) {
        BSLS_ASSERT(bdlde::Utf8Util::isValid(iter->first.data(),
                                             iter->first.size()));
    }
#endif
}
#endif

// MANIPULATORS
inline
JsonObject& JsonObject::operator=(const JsonObject& rhs)
{
    d_members = rhs.d_members;
    return *this;
}

inline
JsonObject& JsonObject::operator=(bslmf::MovableRef<JsonObject> rhs)
{
    Container& rhsMembers = bslmf::MovableRefUtil::access(rhs).d_members;
    if (d_members.get_allocator() == rhsMembers.get_allocator()) {
        d_members = bslmf::MovableRefUtil::move(rhsMembers);
    }
    else {
        d_members = rhsMembers;
    }
    return *this;
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
inline
JsonObject& JsonObject::operator=(std::initializer_list<Member> members)
{
    d_members = members;
    return *this;
}
#endif

inline
Json& JsonObject::operator[](const bsl::string_view& key)
{
    BSLS_ASSERT(bdlde::Utf8Util::isValid(key.data(), key.size()));
    Iterator it = d_members.find(key);
    if (it != d_members.end()) {
        return it->second;                                            // RETURN
    }
    return d_members[bsl::string(key)];
}

inline
JsonObject::Iterator JsonObject::begin() BSLS_KEYWORD_NOEXCEPT
{
    return d_members.begin();
}

inline
void JsonObject::clear() BSLS_KEYWORD_NOEXCEPT
{
    d_members.clear();
}

inline
JsonObject::Iterator JsonObject::end() BSLS_KEYWORD_NOEXCEPT
{
    return d_members.end();
}

inline
bsl::size_t JsonObject::erase(const bsl::string_view& key)
{
    Iterator it = d_members.find(key);
    if (it == d_members.end()) {
        return 0;                                                     // RETURN
    }
    d_members.erase(it);
    return 1;
}

inline
JsonObject::Iterator JsonObject::erase(Iterator position)
{
    return d_members.erase(position);
}

inline
JsonObject::Iterator JsonObject::erase(ConstIterator position)
{
    return d_members.erase(position);
}

inline
JsonObject::Iterator JsonObject::find(const bsl::string_view& key)
{
    return d_members.find(key);
}

inline
bsl::pair<JsonObject::Iterator, bool> JsonObject::insert(const Member& member)
{
    return d_members.insert(member);
}

inline
bsl::pair<JsonObject::Iterator, bool> JsonObject::insert(
                                              bslmf::MovableRef<Member> member)
{
#ifdef BSLS_ASSERT_IS_ACTIVE
    const bsl::string& key = bslmf::MovableRefUtil::access(member).first;
    BSLS_ASSERT(bdlde::Utf8Util::isValid(key.data(), key.size()));
#endif
    return d_members.insert(bslmf::MovableRefUtil::move(member));
}

template <class INPUT_ITERATOR>
inline
typename bsl::enable_if<
    bsl::is_convertible<
        typename bsl::iterator_traits<INPUT_ITERATOR>::value_type::first_type,
        const bsl::string>::value &&
        bsl::is_convertible<typename bsl::iterator_traits<
                                INPUT_ITERATOR>::value_type::second_type,
                            bdljsn::Json>::value,
    void>::type
JsonObject::insert(INPUT_ITERATOR first, INPUT_ITERATOR last)
{
    d_members.insert(first, last);
#ifdef BSLS_ASSERT_SAFE_IS_ACTIVE
    for (Container::const_iterator iter = d_members.begin();
         iter != d_members.end();
         ++iter) {
        BSLS_ASSERT_SAFE(
             bdlde::Utf8Util::isValid(iter->first.data(), iter->first.size()));
    }
#endif
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
inline
void JsonObject::insert(std::initializer_list<Member> members)
{
#ifdef BSLS_ASSERT_IS_ACTIVE
    for (auto member : members) {
        BSLS_ASSERT(bdlde::Utf8Util::isValid(member.first.data(),
                                             member.first.size()));
    }
#endif
    d_members.insert(members);
}
#endif

template <class VALUE>
bsl::pair<JsonObject::Iterator, bool> JsonObject::insert(
                                const bsl::string_view&                  key,
                                BSLS_COMPILERFEATURES_FORWARD_REF(VALUE) value)
{
    BSLS_ASSERT(bdlde::Utf8Util::isValid(key.data(), key.size()));
    Json   json(BSLS_COMPILERFEATURES_FORWARD(VALUE, value), allocator());
    Member member(key, Json(), allocator());
    member.second = bslmf::MovableRefUtil::move(json);
    return insert(bslmf::MovableRefUtil::move(member));
}

inline
void JsonObject::swap(JsonObject& other)
{
    BSLS_ASSERT(allocator() == other.allocator());
    d_members.swap(other.d_members);
}

// ACCESSORS
inline
const Json& JsonObject::operator[](const bsl::string_view& key) const
{
    ConstIterator it = d_members.find(key);
    BSLS_ASSERT(it != d_members.end());
    return it->second;
}

inline
bslma::Allocator *JsonObject::allocator() const BSLS_KEYWORD_NOEXCEPT
{
    return d_members.get_allocator().mechanism();
}

inline
JsonObject::ConstIterator JsonObject::begin() const BSLS_KEYWORD_NOEXCEPT
{
    return d_members.begin();
}

inline
JsonObject::ConstIterator JsonObject::cbegin() const BSLS_KEYWORD_NOEXCEPT
{
    return d_members.cbegin();
}

inline
JsonObject::ConstIterator JsonObject::cend() const BSLS_KEYWORD_NOEXCEPT
{
    return d_members.cend();
}

inline
bool JsonObject::contains(const bsl::string_view& key) const
{
    return d_members.find(key) != d_members.end();
}

inline
bool JsonObject::empty() const BSLS_KEYWORD_NOEXCEPT
{
    return d_members.empty();
}

inline
JsonObject::ConstIterator JsonObject::end() const BSLS_KEYWORD_NOEXCEPT
{
    return d_members.end();
}

inline
JsonObject::ConstIterator JsonObject::find(const bsl::string_view& key) const
{
    return d_members.find(key);
}

inline
bsl::size_t JsonObject::size() const BSLS_KEYWORD_NOEXCEPT
{
    return d_members.size();
}
                                 // ----------
                                 // class Json
                                 // ----------

// CREATORS
inline
Json::Json()
: d_value(JsonNull())
{
}

inline
Json::Json(bslma::Allocator *basicAllocator)
: d_value(JsonNull(), basicAllocator)
{
}

inline
Json::Json(const Json& original, bslma::Allocator *basicAllocator)
: d_value(original.d_value, basicAllocator)
{
}

inline
Json::Json(bslmf::MovableRef<Json> original)
: d_value(bslmf::MovableRefUtil::move(
                              bslmf::MovableRefUtil::access(original).d_value))
{
}

inline
Json::Json(bslmf::MovableRef<Json> original, bslma::Allocator *basicAllocator)
: d_value(bslmf::MovableRefUtil::move(
                              bslmf::MovableRefUtil::access(original).d_value),
          basicAllocator)
{
}

inline
Json::Json(const JsonArray& array, bslma::Allocator *basicAllocator)
: d_value(array, basicAllocator)
{
}

inline
Json::Json(bslmf::MovableRef<JsonArray>  array,
           bslma::Allocator             *basicAllocator)
: d_value(bslmf::MovableRefUtil::move(array), basicAllocator)
{
}

inline
Json::Json(bool boolean, bslma::Allocator *basicAllocator)
: d_value(boolean, basicAllocator)
{
}

inline
Json::Json(const JsonNull& null, bslma::Allocator *basicAllocator)
: d_value(null, basicAllocator)
{
}

inline
Json::Json(float number, bslma::Allocator *basicAllocator)
: d_value(JsonNumber(number), basicAllocator)
{
}

inline
Json::Json(double number, bslma::Allocator *basicAllocator)
: d_value(JsonNumber(number), basicAllocator)
{
}

inline
Json::Json(bdldfp::Decimal64 number, bslma::Allocator *basicAllocator)
: d_value(JsonNumber(number), basicAllocator)
{
}

inline
Json::Json(int number, bslma::Allocator *basicAllocator)
: d_value(JsonNumber(number), basicAllocator)
{
}

inline
Json::Json(unsigned int number, bslma::Allocator *basicAllocator)
: d_value(JsonNumber(number), basicAllocator)
{
}

inline
Json::Json(bsls::Types::Int64 number, bslma::Allocator *basicAllocator)
: d_value(JsonNumber(number), basicAllocator)
{
}

inline
Json::Json(bsls::Types::Uint64 number, bslma::Allocator *basicAllocator)
: d_value(JsonNumber(number), basicAllocator)
{
}

inline
Json::Json(const JsonNumber& number, bslma::Allocator *basicAllocator)
: d_value(number, basicAllocator)
{
}

inline
Json::Json(bslmf::MovableRef<JsonNumber>  number,
           bslma::Allocator              *basicAllocator)
: d_value(bslmf::MovableRefUtil::move(number), basicAllocator)
{
}

inline
Json::Json(const JsonObject& object, bslma::Allocator *basicAllocator)
: d_value(object, basicAllocator)
{
}

inline
Json::Json(bslmf::MovableRef<JsonObject>  object,
           bslma::Allocator              *basicAllocator)
: d_value(bslmf::MovableRefUtil::move(object), basicAllocator)
{
}

inline
Json::Json(const char *string, bslma::Allocator *basicAllocator)
: d_value(basicAllocator)
{
    BSLS_ASSERT(bdlde::Utf8Util::isValid(string));
    d_value.createInPlace<bsl::string>(string);
}

inline
Json::Json(const bsl::string_view& string, bslma::Allocator *basicAllocator)
: d_value(basicAllocator)
{
    BSLS_ASSERT(bdlde::Utf8Util::isValid(string));
    d_value.createInPlace<bsl::string>(string);
}

template <class STRING_TYPE>
inline
Json::Json(BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) string,
           bslma::Allocator                    *basicAllocator,
           typename bsl::enable_if<
               bsl::is_same<STRING_TYPE, bsl::string>::value>::type *)
: d_value(bslmf::MovableRefUtil::move(string), basicAllocator)
{
    BSLS_ASSERT(bdlde::Utf8Util::isValid(
                   bslmf::MovableRefUtil::access(d_value.the<bsl::string>())));
}

// MANIPULATORS
inline
Json& Json::operator=(const Json& rhs)
{
    d_value = rhs.d_value;
    return *this;
}

inline
Json& Json::operator=(bslmf::MovableRef<Json> rhs)
{
    Json& rhsRef = bslmf::MovableRefUtil::access(rhs);
    d_value      = bslmf::MovableRefUtil::move(rhsRef.d_value);
    return *this;
}

inline
Json& Json::operator=(float rhs)
{
    d_value.createInPlace<JsonNumber>(rhs);
    return *this;
}

inline
Json& Json::operator=(double rhs)
{
    d_value.createInPlace<JsonNumber>(rhs);
    return *this;
}

inline
Json& Json::operator=(bdldfp::Decimal64 rhs)
{
    d_value.createInPlace<JsonNumber>(rhs);
    return *this;
}

inline
Json& Json::operator=(int rhs)
{
    d_value.createInPlace<JsonNumber>(rhs);
    return *this;
}

inline
Json& Json::operator=(unsigned int rhs)
{
    d_value.createInPlace<JsonNumber>(rhs);
    return *this;
}

inline
Json& Json::operator=(bsls::Types::Int64 rhs)
{
    d_value.createInPlace<JsonNumber>(rhs);
    return *this;
}

inline
Json& Json::operator=(bsls::Types::Uint64 rhs)
{
    d_value.createInPlace<JsonNumber>(rhs);
    return *this;
}

inline
Json& Json::operator=(const JsonNumber& rhs)
{
    d_value.createInPlace<JsonNumber>(rhs);
    return *this;
}

inline
Json& Json::operator=(bslmf::MovableRef<JsonNumber> rhs)
{
    d_value = bslmf::MovableRefUtil::move(rhs);
    return *this;
}

inline
Json& Json::operator=(const char *rhs)
{
    makeString(rhs);
    return *this;
}

inline
Json& Json::operator=(const bsl::string_view& rhs)
{
    makeString(rhs);
    return *this;
}

template <class STRING_TYPE>
typename bsl::enable_if<bsl::is_same<STRING_TYPE, bsl::string>::value,
                        Json>::type&
    Json::operator=(BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) rhs)
{
    makeString(bslmf::MovableRefUtil::move(rhs));
    return *this;
}

inline
Json& Json::operator=(bool rhs)
{
    makeBoolean(rhs);
    return *this;
}

inline
Json& Json::operator=(const JsonObject& rhs)
{
    makeObject(rhs);
    return *this;
}

inline
Json& Json::operator=(bslmf::MovableRef<JsonObject> rhs)
{
    makeObject(bslmf::MovableRefUtil::move(rhs));
    return *this;
}

inline
Json& Json::operator=(const JsonArray& rhs)
{
    makeArray(rhs);
    return *this;
}

inline
Json& Json::operator=(bslmf::MovableRef<JsonArray> rhs)
{
    makeArray(bslmf::MovableRefUtil::move(rhs));
    return *this;
}

inline
Json& Json::operator=(const JsonNull&)
{
    makeNull();
    return *this;
}

inline
Json& Json::operator=(bslmf::MovableRef<JsonNull>)
{
    makeNull();
    return *this;
}

inline
JsonArray& Json::makeArray()
{
    return d_value.createInPlace<JsonArray>();
}

inline
JsonArray& Json::makeArray(const JsonArray& array)
{
    d_value = array;
    return d_value.the<JsonArray>();
}

inline
JsonArray& Json::makeArray(bslmf::MovableRef<JsonArray> array)
{
    d_value = bslmf::MovableRefUtil::move(array);
    return d_value.the<JsonArray>();
}

inline
bool& Json::makeBoolean()
{
    return d_value.createInPlace<bool>();
}

inline
bool& Json::makeBoolean(bool boolean)
{
    d_value = boolean;
    return d_value.the<bool>();
}

inline
void Json::makeNull()
{
    d_value.createInPlace<JsonNull>();
}

inline
JsonNumber& Json::makeNumber()
{
    return d_value.createInPlace<JsonNumber>();
}

inline
JsonNumber& Json::makeNumber(const JsonNumber& number)
{
    d_value = number;
    return d_value.the<JsonNumber>();
}

inline
JsonNumber& Json::makeNumber(bslmf::MovableRef<JsonNumber> number)
{
    d_value = bslmf::MovableRefUtil::move(number);
    return d_value.the<JsonNumber>();
}

inline
JsonObject& Json::makeObject()
{
    return d_value.createInPlace<JsonObject>();
}

inline
JsonObject& Json::makeObject(const JsonObject& object)
{
    d_value = object;
    return d_value.the<JsonObject>();
}

inline
JsonObject& Json::makeObject(bslmf::MovableRef<JsonObject> object)
{
    d_value = bslmf::MovableRefUtil::move(object);
    return d_value.the<JsonObject>();
}

inline
void Json::makeString(const char *string)
{
    BSLS_ASSERT(bdlde::Utf8Util::isValid(string));
    d_value.createInPlace<bsl::string>(string);
}

inline
void Json::makeString(const bsl::string_view& string)
{
    BSLS_ASSERT(bdlde::Utf8Util::isValid(string.data(), string.size()));
    d_value.createInPlace<bsl::string>(string);
}

template <class STRING_TYPE>
inline
typename bsl::enable_if<bsl::is_same<STRING_TYPE, bsl::string>::value>::type
    Json::makeString(BSLMF_MOVABLEREF_DEDUCE(STRING_TYPE) string)
{
    BSLS_ASSERT(
              bdlde::Utf8Util::isValid(bslmf::MovableRefUtil::access(string)));
    d_value = bslmf::MovableRefUtil::move(string);
}

inline
void Json::swap(Json& other)
{
    BSLS_ASSERT(allocator() == other.allocator());
    d_value.swap(other.d_value);
}

// ACCESSORS
inline
bool Json::isArray() const
{
    return type() == JsonType::e_ARRAY;
}

inline
bool Json::isBoolean() const
{
    return type() == JsonType::e_BOOLEAN;
}

inline
bool Json::isNull() const
{
    return type() == JsonType::e_NULL;
}

inline
bool Json::isNumber() const
{
    return type() == JsonType::e_NUMBER;
}

inline
bool Json::isObject() const
{
    return type() == JsonType::e_OBJECT;
}

inline
bool Json::isString() const
{
    return type() == JsonType::e_STRING;
}

inline
JsonArray& Json::theArray()
{
    return d_value.the<JsonArray>();
}

inline
bool& Json::theBoolean()
{
    return d_value.the<bool>();
}

inline
JsonNull& Json::theNull()
{
    return d_value.the<JsonNull>();
}

inline
JsonNumber& Json::theNumber()
{
    return d_value.the<JsonNumber>();
}

inline
JsonObject& Json::theObject()
{
    return d_value.the<JsonObject>();
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_OPERATOR_EXPLICIT)
inline
Json::operator JsonArray &()
{
    return theArray();
}

inline
Json::operator bool &()
{
    return theBoolean();
}

inline
Json::operator JsonNull &()
{
    return theNull();
}

inline
Json::operator JsonNumber &()
{
    return theNumber();
}

inline
Json::operator JsonObject &()
{
    return theObject();
}
#endif

inline
Json& Json::operator[](const bsl::string_view& key)
{
    BSLS_ASSERT(d_value.is<JsonObject>());
    return theObject()[key];
}

inline
Json& Json::operator[](bsl::size_t index)
{
    BSLS_ASSERT(d_value.is<JsonArray>());
    return theArray()[index];
}

inline
bsl::size_t Json::size() const
{
    BSLS_ASSERT(isArray() || isObject());
    return isArray() ? theArray().size() : theObject().size();
}

// ACCESSORS
inline
bslma::Allocator *Json::allocator() const BSLS_KEYWORD_NOEXCEPT
{
    return d_value.getAllocator();
}

inline
bdldfp::Decimal64 Json::asDecimal64() const
{
    BSLS_ASSERT(isNumber());
    return theNumber().asDecimal64();
}

inline
int Json::asDecimal64Exact(bdldfp::Decimal64 *result) const
{
    BSLS_ASSERT(isNumber());
    return theNumber().asDecimal64Exact(result);
}

inline
double Json::asDouble() const
{
    BSLS_ASSERT(isNumber());
    return theNumber().asDouble();
}

inline
float Json::asFloat() const
{
    BSLS_ASSERT(isNumber());
    return theNumber().asFloat();
}

inline
int Json::asInt(int *result) const
{
    BSLS_ASSERT(isNumber());
    return theNumber().asInt(result);
}

inline
int Json::asInt64(bsls::Types::Int64 *result) const
{
    BSLS_ASSERT(isNumber());
    return theNumber().asInt64(result);
}

inline
int Json::asUint(unsigned int *result) const
{
    BSLS_ASSERT(isNumber());
    return theNumber().asUint(result);
}

inline
int Json::asUint64(bsls::Types::Uint64 *result) const
{
    BSLS_ASSERT(isNumber());
    return theNumber().asUint64(result);
}


inline
const JsonArray& Json::theArray() const
{
    return d_value.the<JsonArray>();
}

inline
const bool& Json::theBoolean() const
{
    return d_value.the<bool>();
}

inline
const JsonNull& Json::theNull() const
{
    return d_value.the<JsonNull>();
}

inline
const JsonNumber& Json::theNumber() const
{
    return d_value.the<JsonNumber>();
}

inline
const JsonObject& Json::theObject() const
{
    return d_value.the<JsonObject>();
}

inline
const bsl::string& Json::theString() const
{
    return d_value.the<bsl::string>();
}

inline
JsonType::Enum Json::type() const BSLS_KEYWORD_NOEXCEPT
{
    return static_cast<JsonType::Enum>(d_value.typeIndex() - 1);
}
inline
const Json& Json::operator[](const bsl::string_view& key) const
{
    BSLS_ASSERT(d_value.is<JsonObject>());
    return theObject()[key];
}

inline
const Json& Json::operator[](bsl::size_t index) const
{
    BSLS_ASSERT(d_value.is<JsonArray>());
    return theArray()[index];
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_OPERATOR_EXPLICIT)
inline
Json::operator const JsonArray &() const
{
    return theArray();
}

inline
Json::operator const bool &() const
{
    return theBoolean();
}

inline
Json::operator const JsonNull &() const
{
    return theNull();
}

inline
Json::operator const JsonNumber &() const
{
    return theNumber();
}

inline
Json::operator const JsonObject &() const
{
    return theObject();
}

inline
Json::operator const bsl::string &() const
{
    return theString();
}
#endif

}  // close package namespace

// FREE OPERATORS
inline
bsl::ostream& bdljsn::operator<<(bsl::ostream&            stream,
                                 const bdljsn::JsonArray& object)
{
    return object.print(stream, 0, -1);
}

inline
bool bdljsn::operator==(const bdljsn::JsonArray& lhs,
                        const bdljsn::JsonArray& rhs)
{
    return lhs.d_elements == rhs.d_elements;
}

inline
bool bdljsn::operator!=(const bdljsn::JsonArray& lhs,
                        const bdljsn::JsonArray& rhs)
{
    return lhs.d_elements != rhs.d_elements;
}

template <class HASHALG>
inline
void bdljsn::hashAppend(HASHALG& hashAlg, const bdljsn::JsonArray& object)
{
    hashAppend(hashAlg, object.d_elements);
}

inline
void bdljsn::swap(bdljsn::JsonArray& a, bdljsn::JsonArray& b)
{
    bslalg::SwapUtil::swap(&a.d_elements, &b.d_elements);
}

inline
bsl::ostream& bdljsn::operator<<(bsl::ostream&             stream,
                                 const bdljsn::JsonObject& object)
{
    return object.print(stream, 0, -1);
}

inline
bool bdljsn::operator==(const bdljsn::JsonObject& lhs,
                        const bdljsn::JsonObject& rhs)
{
    return lhs.d_members == rhs.d_members;
}

inline
bool bdljsn::operator!=(const bdljsn::JsonObject& lhs,
                        const bdljsn::JsonObject& rhs)
{
    return lhs.d_members != rhs.d_members;
}

template <class HASHALG>
inline
void bdljsn::hashAppend(HASHALG& hashAlg, const bdljsn::JsonObject& object)
{
    hashAppend(hashAlg, object.d_members);
}

inline
void bdljsn::swap(bdljsn::JsonObject& a, bdljsn::JsonObject& b)
{
    if (a.allocator() == b.allocator()) {
        bslalg::SwapUtil::swap(&a.d_members, &b.d_members);
    }
    else {
        bslma::Allocator *const allocA = a.allocator();
        bslma::Allocator *const allocB = b.allocator();
        JsonObject              ta(b, allocA);
        JsonObject              tb(a, allocB);
        swap(a, ta);
        swap(b, tb);
    }
}

inline
bsl::ostream& bdljsn::operator<<(bsl::ostream&       stream,
                                 const bdljsn::Json& object)
{
    return object.print(stream, 0, -1);
}

inline
bool bdljsn::operator==(const bdljsn::Json& lhs, const bdljsn::Json& rhs)
{
    return lhs.d_value == rhs.d_value;
}

inline
bool bdljsn::operator!=(const bdljsn::Json& lhs, const bdljsn::Json& rhs)
{
    return lhs.d_value != rhs.d_value;
}

template <class HASHALG>
inline
void bdljsn::hashAppend(HASHALG& hashAlg, const bdljsn::Json& object)
{
    hashAppend(hashAlg, object.d_value);
}

inline
void bdljsn::swap(bdljsn::Json& a, bdljsn::Json& b)
{
    bslalg::SwapUtil::swap(&a.d_value, &b.d_value);
}

}  // close enterprise namespace

#endif  // INCLUDED_BDLJSN_JSON

// ----------------------------------------------------------------------------
// Copyright 2022 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------