/*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * HTTP Library: JSON parser and writer * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef CASA_JSON_H #define CASA_JSON_H #include "cpprest/asyncrt_utils.h" #include "cpprest/details/basic_types.h" #include #include #include #include #include #include namespace web { /// Library for parsing and serializing JSON values to and from C++ types. namespace json { // Various forward declarations. namespace details { class _Value; class _Number; class _Null; class _Boolean; class _String; class _Object; class _Array; template class JSON_Parser; } // namespace details namespace details { extern bool g_keep_json_object_unsorted; } /// /// Preserve the order of the name/value pairs when parsing a JSON object. /// The default is false, which can yield better performance. /// /// true if ordering should be preserved when parsing, false otherwise. /// Note this is a global setting and affects all JSON parsing done. void _ASYNCRTIMP __cdecl keep_object_element_order(bool keep_order); #ifdef _WIN32 #ifdef _DEBUG #define ENABLE_JSON_VALUE_VISUALIZER #endif #endif class number; class array; class object; /// /// A JSON value represented as a C++ class. /// class value { public: /// /// This enumeration represents the various kinds of JSON values. /// enum value_type { /// Number value Number, /// Boolean value Boolean, /// String value String, /// Object value Object, /// Array value Array, /// Null value Null }; /// /// Constructor creating a null value /// _ASYNCRTIMP value(); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from _ASYNCRTIMP value(int32_t value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from _ASYNCRTIMP value(uint32_t value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from _ASYNCRTIMP value(int64_t value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from _ASYNCRTIMP value(uint64_t value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from _ASYNCRTIMP value(double value); /// /// Constructor creating a JSON Boolean value /// /// The C++ value to create a JSON value from _ASYNCRTIMP explicit value(bool value); /// /// Constructor creating a JSON string value /// /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character /// width This constructor has O(n) performance because it tries to determine if specified string /// has characters that should be properly escaped in JSON. _ASYNCRTIMP explicit value(utility::string_t value); /// /// Constructor creating a JSON string value specifying if the string contains characters to escape /// /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character /// width Whether contains characters that should /// be escaped in JSON value This constructor has O(1) performance. /// _ASYNCRTIMP explicit value(utility::string_t value, bool has_escape_chars); /// /// Constructor creating a JSON string value /// /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character /// width This constructor has O(n) performance because it tries to determine if specified /// string has characters that should be properly escaped in JSON. /// /// /// This constructor exists in order to avoid string literals matching another constructor, /// as is very likely. For example, conversion to bool does not require a user-defined conversion, /// and will therefore match first, which means that the JSON value turns up as a boolean. /// /// _ASYNCRTIMP explicit value(const utility::char_t* value); /// /// Constructor creating a JSON string value /// /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character /// width Whether contains characters /// /// This overload has O(1) performance. /// /// /// This constructor exists in order to avoid string literals matching another constructor, /// as is very likely. For example, conversion to bool does not require a user-defined conversion, /// and will therefore match first, which means that the JSON value turns up as a boolean. /// /// _ASYNCRTIMP explicit value(const utility::char_t* value, bool has_escape_chars); /// /// Copy constructor /// _ASYNCRTIMP value(const value&); /// /// Move constructor /// _ASYNCRTIMP value(value&&) CPPREST_NOEXCEPT; /// /// Assignment operator. /// /// The JSON value object that contains the result of the assignment. _ASYNCRTIMP value& operator=(const value&); /// /// Move assignment operator. /// /// The JSON value object that contains the result of the assignment. _ASYNCRTIMP value& operator=(value&&) CPPREST_NOEXCEPT; // Static factories /// /// Creates a null value /// /// A JSON null value static _ASYNCRTIMP value __cdecl null(); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value static _ASYNCRTIMP value __cdecl number(double value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value static _ASYNCRTIMP value __cdecl number(int32_t value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value static _ASYNCRTIMP value __cdecl number(uint32_t value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value static _ASYNCRTIMP value __cdecl number(int64_t value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value static _ASYNCRTIMP value __cdecl number(uint64_t value); /// /// Creates a Boolean value /// /// The C++ value to create a JSON value from /// A JSON Boolean value static _ASYNCRTIMP value __cdecl boolean(bool value); /// /// Creates a string value /// /// The C++ value to create a JSON value from /// A JSON string value /// /// This overload has O(n) performance because it tries to determine if /// specified string has characters that should be properly escaped in JSON. /// static _ASYNCRTIMP value __cdecl string(utility::string_t value); /// /// Creates a string value specifying if the string contains characters to escape /// /// The C++ value to create a JSON value from /// Whether contains characters /// that should be escaped in JSON value /// A JSON string value /// /// This overload has O(1) performance. /// static _ASYNCRTIMP value __cdecl string(utility::string_t value, bool has_escape_chars); #ifdef _WIN32 private: // Only used internally by JSON parser. static _ASYNCRTIMP value __cdecl string(const std::string& value); public: #endif /// /// Creates an object value /// /// Whether to preserve the original order of the fields /// An empty JSON object value static _ASYNCRTIMP json::value __cdecl object(bool keep_order = false); /// /// Creates an object value from a collection of field/values /// /// Field names associated with JSON values /// Whether to preserve the original order of the fields /// A non-empty JSON object value static _ASYNCRTIMP json::value __cdecl object(std::vector> fields, bool keep_order = false); /// /// Creates an empty JSON array /// /// An empty JSON array value static _ASYNCRTIMP json::value __cdecl array(); /// /// Creates a JSON array /// /// The initial number of elements of the JSON value /// A JSON array value static _ASYNCRTIMP json::value __cdecl array(size_t size); /// /// Creates a JSON array /// /// A vector of JSON values /// A JSON array value static _ASYNCRTIMP json::value __cdecl array(std::vector elements); /// /// Accesses the type of JSON value the current value instance is /// /// The value's type _ASYNCRTIMP json::value::value_type type() const; /// /// Is the current value a null value? /// /// true if the value is a null value, false otherwise bool is_null() const { return type() == Null; }; /// /// Is the current value a number value? /// /// true if the value is a number value, false otherwise bool is_number() const { return type() == Number; } /// /// Is the current value represented as an integer number value? /// /// /// Note that if a json value is a number but represented as a double it can still /// be retrieved as a integer using as_integer(), however the value will be truncated. /// /// true if the value is an integer value, false otherwise. _ASYNCRTIMP bool is_integer() const; /// /// Is the current value represented as an double number value? /// /// /// Note that if a json value is a number but represented as a int it can still /// be retrieved as a double using as_double(). /// /// true if the value is an double value, false otherwise. _ASYNCRTIMP bool is_double() const; /// /// Is the current value a Boolean value? /// /// true if the value is a Boolean value, false otherwise bool is_boolean() const { return type() == Boolean; } /// /// Is the current value a string value? /// /// true if the value is a string value, false otherwise bool is_string() const { return type() == String; } /// /// Is the current value an array? /// /// true if the value is an array, false otherwise bool is_array() const { return type() == Array; } /// /// Is the current value an object? /// /// true if the value is an object, false otherwise bool is_object() const { return type() == Object; } /// /// Gets the number of children of the value. /// /// The number of children. 0 for all non-composites. size_t size() const; /// /// Parses a string and construct a JSON value. /// /// The C++ value to create a JSON value from, a C++ STL double-byte string _ASYNCRTIMP static value __cdecl parse(const utility::string_t& value); /// /// Attempts to parse a string and construct a JSON value. /// /// The C++ value to create a JSON value from, a C++ STL double-byte string /// If parsing fails, the error code is greater than 0 /// The parsed object. Returns web::json::value::null if failed _ASYNCRTIMP static value __cdecl parse(const utility::string_t& value, std::error_code& errorCode); /// /// Serializes the current JSON value to a C++ string. /// /// A string representation of the value _ASYNCRTIMP utility::string_t serialize() const; /// /// Serializes the current JSON value to a C++ string. /// /// A string representation of the value CASABLANCA_DEPRECATED("This API is deprecated and has been renamed to avoid confusion with as_string(), use " "::web::json::value::serialize() instead.") _ASYNCRTIMP utility::string_t to_string() const; /// /// Parses a JSON value from the contents of an input stream using the native platform character width. /// /// The stream to read the JSON value from /// The JSON value object created from the input stream. _ASYNCRTIMP static value __cdecl parse(utility::istream_t& input); /// /// Parses a JSON value from the contents of an input stream using the native platform character width. /// /// The stream to read the JSON value from /// If parsing fails, the error code is greater than 0 /// The parsed object. Returns web::json::value::null if failed _ASYNCRTIMP static value __cdecl parse(utility::istream_t& input, std::error_code& errorCode); /// /// Writes the current JSON value to a stream with the native platform character width. /// /// The stream that the JSON string representation should be written to. _ASYNCRTIMP void serialize(utility::ostream_t& stream) const; #ifdef _WIN32 /// /// Parses a JSON value from the contents of a single-byte (UTF8) stream. /// /// The stream to read the JSON value from _ASYNCRTIMP static value __cdecl parse(std::istream& stream); /// /// Parses a JSON value from the contents of a single-byte (UTF8) stream. /// /// The stream to read the JSON value from /// If parsing fails, the error code is greater than 0 /// The parsed object. Returns web::json::value::null if failed _ASYNCRTIMP static value __cdecl parse(std::istream& stream, std::error_code& error); /// /// Serializes the content of the value into a single-byte (UTF8) stream. /// /// The stream that the JSON string representation should be written to. _ASYNCRTIMP void serialize(std::ostream& stream) const; #endif /// /// Converts the JSON value to a C++ double, if and only if it is a number value. /// Throws if the value is not a number /// /// A double representation of the value _ASYNCRTIMP double as_double() const; /// /// Converts the JSON value to a C++ integer, if and only if it is a number value. /// Throws if the value is not a number /// /// An integer representation of the value _ASYNCRTIMP int as_integer() const; /// /// Converts the JSON value to a number class, if and only if it is a number value. /// Throws if the value is not a number /// /// An instance of number class _ASYNCRTIMP const json::number& as_number() const; /// /// Converts the JSON value to a C++ bool, if and only if it is a Boolean value. /// /// A C++ bool representation of the value _ASYNCRTIMP bool as_bool() const; /// /// Converts the JSON value to a json array, if and only if it is an array value. /// /// The returned json::array should have the same or shorter lifetime as this /// An array representation of the value _ASYNCRTIMP json::array& as_array(); /// /// Converts the JSON value to a json array, if and only if it is an array value. /// /// The returned json::array should have the same or shorter lifetime as this /// An array representation of the value _ASYNCRTIMP const json::array& as_array() const; /// /// Converts the JSON value to a json object, if and only if it is an object value. /// /// An object representation of the value _ASYNCRTIMP json::object& as_object(); /// /// Converts the JSON value to a json object, if and only if it is an object value. /// /// An object representation of the value _ASYNCRTIMP const json::object& as_object() const; /// /// Converts the JSON value to a C++ STL string, if and only if it is a string value. /// /// A C++ STL string representation of the value _ASYNCRTIMP const utility::string_t& as_string() const; /// /// Compares two JSON values for equality. /// /// The JSON value to compare with. /// True if the values are equal. _ASYNCRTIMP bool operator==(const value& other) const; /// /// Compares two JSON values for inequality. /// /// The JSON value to compare with. /// True if the values are unequal. bool operator!=(const value& other) const { return !((*this) == other); } /// /// Tests for the presence of a field. /// /// The name of the field /// True if the field exists, false otherwise. bool has_field(const utility::string_t& key) const; /// /// Tests for the presence of a number field /// /// The name of the field /// True if the field exists, false otherwise. _ASYNCRTIMP bool has_number_field(const utility::string_t& key) const; /// /// Tests for the presence of an integer field /// /// The name of the field /// True if the field exists, false otherwise. _ASYNCRTIMP bool has_integer_field(const utility::string_t& key) const; /// /// Tests for the presence of a double field /// /// The name of the field /// True if the field exists, false otherwise. _ASYNCRTIMP bool has_double_field(const utility::string_t& key) const; /// /// Tests for the presence of a boolean field /// /// The name of the field /// True if the field exists, false otherwise. _ASYNCRTIMP bool has_boolean_field(const utility::string_t& key) const; /// /// Tests for the presence of a string field /// /// The name of the field /// True if the field exists, false otherwise. _ASYNCRTIMP bool has_string_field(const utility::string_t& key) const; /// /// Tests for the presence of an array field /// /// The name of the field /// True if the field exists, false otherwise. _ASYNCRTIMP bool has_array_field(const utility::string_t& key) const; /// /// Tests for the presence of an object field /// /// The name of the field /// True if the field exists, false otherwise. _ASYNCRTIMP bool has_object_field(const utility::string_t& key) const; /// /// Accesses a field of a JSON object. /// /// The name of the field /// The value kept in the field; null if the field does not exist CASABLANCA_DEPRECATED( "This API is deprecated and will be removed in a future release, use json::value::at() instead.") value get(const utility::string_t& key) const; /// /// Erases an element of a JSON array. Throws if index is out of bounds. /// /// The index of the element to erase in the JSON array. _ASYNCRTIMP void erase(size_t index); /// /// Erases an element of a JSON object. Throws if the key doesn't exist. /// /// The key of the element to erase in the JSON object. _ASYNCRTIMP void erase(const utility::string_t& key); /// /// Accesses an element of a JSON array. Throws when index out of bounds. /// /// The index of an element in the JSON array. /// A reference to the value. _ASYNCRTIMP json::value& at(size_t index); /// /// Accesses an element of a JSON array. Throws when index out of bounds. /// /// The index of an element in the JSON array. /// A reference to the value. _ASYNCRTIMP const json::value& at(size_t index) const; /// /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value. _ASYNCRTIMP json::value& at(const utility::string_t& key); /// /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value. _ASYNCRTIMP const json::value& at(const utility::string_t& key) const; /// /// Accesses a field of a JSON object. /// /// The name of the field /// A reference to the value kept in the field. _ASYNCRTIMP value& operator[](const utility::string_t& key); #ifdef _WIN32 private: // Only used internally by JSON parser _ASYNCRTIMP value& operator[](const std::string& key) { // JSON object stores its field map as a unordered_map of string_t, so this conversion is hard to avoid return operator[](utility::conversions::to_string_t(key)); } public: #endif /// /// Accesses an element of a JSON array. /// /// The index of an element in the JSON array /// The value kept at the array index; null if outside the boundaries of the array CASABLANCA_DEPRECATED( "This API is deprecated and will be removed in a future release, use json::value::at() instead.") value get(size_t index) const; /// /// Accesses an element of a JSON array. /// /// The index of an element in the JSON array. /// A reference to the value kept in the field. _ASYNCRTIMP value& operator[](size_t index); private: friend class web::json::details::_Object; friend class web::json::details::_Array; template friend class web::json::details::JSON_Parser; #ifdef _WIN32 /// /// Writes the current JSON value as a double-byte string to a string instance. /// /// The string that the JSON representation should be written to. _ASYNCRTIMP void format(std::basic_string& string) const; #endif /// /// Serializes the content of the value into a string instance in UTF8 format /// /// The string that the JSON representation should be written to _ASYNCRTIMP void format(std::basic_string& string) const; #ifdef ENABLE_JSON_VALUE_VISUALIZER explicit value(std::unique_ptr v, value_type kind) : m_value(std::move(v)), m_kind(kind) #else explicit value(std::unique_ptr v) : m_value(std::move(v)) #endif { } std::unique_ptr m_value; #ifdef ENABLE_JSON_VALUE_VISUALIZER value_type m_kind; #endif }; /// /// A single exception type to represent errors in parsing, converting, and accessing /// elements of JSON values. /// class json_exception : public std::exception { private: std::string _message; public: json_exception(const char* const message) : _message(message) {} #ifdef _UTF16_STRINGS json_exception(const wchar_t* const message) : _message(utility::conversions::utf16_to_utf8(message)) {} #endif // _UTF16_STRINGS json_exception(std::string&& message) : _message(std::move(message)) {} // Must be narrow string because it derives from std::exception const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); } }; namespace details { enum json_error { left_over_character_in_stream = 1, malformed_array_literal, malformed_comment, malformed_literal, malformed_object_literal, malformed_numeric_literal, malformed_string_literal, malformed_token, mismatched_brances, nesting, unexpected_token }; class json_error_category_impl : public std::error_category { public: virtual const char* name() const CPPREST_NOEXCEPT override { return "json"; } virtual std::string message(int ev) const override { switch (ev) { case json_error::left_over_character_in_stream: return "Left-over characters in stream after parsing a JSON value"; case json_error::malformed_array_literal: return "Malformed array literal"; case json_error::malformed_comment: return "Malformed comment"; case json_error::malformed_literal: return "Malformed literal"; case json_error::malformed_object_literal: return "Malformed object literal"; case json_error::malformed_numeric_literal: return "Malformed numeric literal"; case json_error::malformed_string_literal: return "Malformed string literal"; case json_error::malformed_token: return "Malformed token"; case json_error::mismatched_brances: return "Mismatched braces"; case json_error::nesting: return "Nesting too deep"; case json_error::unexpected_token: return "Unexpected token"; default: return "Unknown json error"; } } }; const json_error_category_impl& json_error_category(); } // namespace details /// /// A JSON array represented as a C++ class. /// class array { typedef std::vector storage_type; public: typedef storage_type::iterator iterator; typedef storage_type::const_iterator const_iterator; typedef storage_type::reverse_iterator reverse_iterator; typedef storage_type::const_reverse_iterator const_reverse_iterator; typedef storage_type::size_type size_type; private: array() : m_elements() {} array(size_type size) : m_elements(size) {} array(storage_type elements) : m_elements(std::move(elements)) {} public: /// /// Gets the beginning iterator element of the array /// /// An iterator to the beginning of the JSON array. iterator begin() { return m_elements.begin(); } /// /// Gets the beginning const iterator element of the array. /// /// A const_iterator to the beginning of the JSON array. const_iterator begin() const { return m_elements.cbegin(); } /// /// Gets the end iterator element of the array /// /// An iterator to the end of the JSON array. iterator end() { return m_elements.end(); } /// /// Gets the end const iterator element of the array. /// /// A const_iterator to the end of the JSON array. const_iterator end() const { return m_elements.cend(); } /// /// Gets the beginning reverse iterator element of the array /// /// An reverse_iterator to the beginning of the JSON array. reverse_iterator rbegin() { return m_elements.rbegin(); } /// /// Gets the beginning const reverse iterator element of the array /// /// An const_reverse_iterator to the beginning of the JSON array. const_reverse_iterator rbegin() const { return m_elements.rbegin(); } /// /// Gets the end reverse iterator element of the array /// /// An reverse_iterator to the end of the JSON array. reverse_iterator rend() { return m_elements.rend(); } /// /// Gets the end const reverse iterator element of the array /// /// An const_reverse_iterator to the end of the JSON array. const_reverse_iterator rend() const { return m_elements.crend(); } /// /// Gets the beginning const iterator element of the array. /// /// A const_iterator to the beginning of the JSON array. const_iterator cbegin() const { return m_elements.cbegin(); } /// /// Gets the end const iterator element of the array. /// /// A const_iterator to the end of the JSON array. const_iterator cend() const { return m_elements.cend(); } /// /// Gets the beginning const reverse iterator element of the array. /// /// A const_reverse_iterator to the beginning of the JSON array. const_reverse_iterator crbegin() const { return m_elements.crbegin(); } /// /// Gets the end const reverse iterator element of the array. /// /// A const_reverse_iterator to the end of the JSON array. const_reverse_iterator crend() const { return m_elements.crend(); } /// /// Deletes an element of the JSON array. /// /// A const_iterator to the element to delete. /// Iterator to the new location of the element following the erased element. /// GCC doesn't support erase with const_iterator on vector yet. In the future this should be /// changed. iterator erase(iterator position) { return m_elements.erase(position); } /// /// Deletes the element at an index of the JSON array. /// /// The index of the element to delete. void erase(size_type index) { if (index >= m_elements.size()) { throw json_exception("index out of bounds"); } m_elements.erase(m_elements.begin() + index); } /// /// Accesses an element of a JSON array. Throws when index out of bounds. /// /// The index of an element in the JSON array. /// A reference to the value kept in the field. json::value& at(size_type index) { if (index >= m_elements.size()) throw json_exception("index out of bounds"); return m_elements[index]; } /// /// Accesses an element of a JSON array. Throws when index out of bounds. /// /// The index of an element in the JSON array. /// A reference to the value kept in the field. const json::value& at(size_type index) const { if (index >= m_elements.size()) throw json_exception("index out of bounds"); return m_elements[index]; } /// /// Accesses an element of a JSON array. /// /// The index of an element in the JSON array. /// A reference to the value kept in the field. json::value& operator[](size_type index) { msl::safeint3::SafeInt nMinSize(index); nMinSize += 1; msl::safeint3::SafeInt nlastSize(m_elements.size()); if (nlastSize < nMinSize) m_elements.resize(nMinSize); return m_elements[index]; } /// /// Gets the number of elements of the array. /// /// The number of elements. size_type size() const { return m_elements.size(); } private: storage_type m_elements; friend class details::_Array; template friend class json::details::JSON_Parser; }; /// /// A JSON object represented as a C++ class. /// class object { typedef std::vector> storage_type; public: typedef storage_type::iterator iterator; typedef storage_type::const_iterator const_iterator; typedef storage_type::reverse_iterator reverse_iterator; typedef storage_type::const_reverse_iterator const_reverse_iterator; typedef storage_type::size_type size_type; private: object(bool keep_order = false) : m_elements(), m_keep_order(keep_order) {} object(storage_type elements, bool keep_order = false) : m_elements(std::move(elements)), m_keep_order(keep_order) { if (!keep_order) { sort(m_elements.begin(), m_elements.end(), compare_pairs); } } public: /// /// Gets the beginning iterator element of the object /// /// An iterator to the beginning of the JSON object. iterator begin() { return m_elements.begin(); } /// /// Gets the beginning const iterator element of the object. /// /// A const_iterator to the beginning of the JSON object. const_iterator begin() const { return m_elements.cbegin(); } /// /// Gets the end iterator element of the object /// /// An iterator to the end of the JSON object. iterator end() { return m_elements.end(); } /// /// Gets the end const iterator element of the object. /// /// A const_iterator to the end of the JSON object. const_iterator end() const { return m_elements.cend(); } /// /// Gets the beginning reverse iterator element of the object /// /// An reverse_iterator to the beginning of the JSON object. reverse_iterator rbegin() { return m_elements.rbegin(); } /// /// Gets the beginning const reverse iterator element of the object /// /// An const_reverse_iterator to the beginning of the JSON object. const_reverse_iterator rbegin() const { return m_elements.rbegin(); } /// /// Gets the end reverse iterator element of the object /// /// An reverse_iterator to the end of the JSON object. reverse_iterator rend() { return m_elements.rend(); } /// /// Gets the end const reverse iterator element of the object /// /// An const_reverse_iterator to the end of the JSON object. const_reverse_iterator rend() const { return m_elements.crend(); } /// /// Gets the beginning const iterator element of the object. /// /// A const_iterator to the beginning of the JSON object. const_iterator cbegin() const { return m_elements.cbegin(); } /// /// Gets the end const iterator element of the object. /// /// A const_iterator to the end of the JSON object. const_iterator cend() const { return m_elements.cend(); } /// /// Gets the beginning const reverse iterator element of the object. /// /// A const_reverse_iterator to the beginning of the JSON object. const_reverse_iterator crbegin() const { return m_elements.crbegin(); } /// /// Gets the end const reverse iterator element of the object. /// /// A const_reverse_iterator to the end of the JSON object. const_reverse_iterator crend() const { return m_elements.crend(); } /// /// Deletes an element of the JSON object. /// /// A const_iterator to the element to delete. /// Iterator to the new location of the element following the erased element. /// GCC doesn't support erase with const_iterator on vector yet. In the future this should be /// changed. iterator erase(iterator position) { return m_elements.erase(position); } /// /// Deletes an element of the JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. void erase(const utility::string_t& key) { auto iter = find_by_key(key); if (iter == m_elements.end()) { throw web::json::json_exception("Key not found"); } m_elements.erase(iter); } /// /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value kept in the field. json::value& at(const utility::string_t& key) { auto iter = find_by_key(key); if (iter == m_elements.end()) { throw web::json::json_exception("Key not found"); } return iter->second; } /// /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value kept in the field. const json::value& at(const utility::string_t& key) const { auto iter = find_by_key(key); if (iter == m_elements.end()) { throw web::json::json_exception("Key not found"); } return iter->second; } /// /// Accesses an element of a JSON object. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value kept in the field, otherwise a newly created null value /// that will be stored for the given key. json::value& operator[](const utility::string_t& key) { auto iter = find_insert_location(key); if (iter == m_elements.end() || key != iter->first) { return m_elements.insert(iter, std::pair(key, value()))->second; } return iter->second; } /// /// Gets an iterator to an element of a JSON object. /// /// The key of an element in the JSON object. /// A const iterator to the value kept in the field. const_iterator find(const utility::string_t& key) const { return find_by_key(key); } /// /// Gets the number of elements of the object. /// /// The number of elements. size_type size() const { return m_elements.size(); } /// /// Checks if there are any elements in the JSON object. /// /// True if empty. bool empty() const { return m_elements.empty(); } private: static bool compare_pairs(const std::pair& p1, const std::pair& p2) { return p1.first < p2.first; } static bool compare_with_key(const std::pair& p1, const utility::string_t& key) { return p1.first < key; } storage_type::iterator find_insert_location(const utility::string_t& key) { if (m_keep_order) { return std::find_if(m_elements.begin(), m_elements.end(), [&key](const std::pair& p) { return p.first == key; }); } else { return std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key); } } storage_type::const_iterator find_by_key(const utility::string_t& key) const { if (m_keep_order) { return std::find_if(m_elements.begin(), m_elements.end(), [&key](const std::pair& p) { return p.first == key; }); } else { auto iter = std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key); if (iter != m_elements.end() && key != iter->first) { return m_elements.end(); } return iter; } } storage_type::iterator find_by_key(const utility::string_t& key) { auto iter = find_insert_location(key); if (iter != m_elements.end() && key != iter->first) { return m_elements.end(); } return iter; } storage_type m_elements; bool m_keep_order; friend class details::_Object; template friend class json::details::JSON_Parser; }; /// /// A JSON number represented as a C++ class. /// class number { // Note that these constructors make sure that only negative integers are stored as signed int64 (while others // convert to unsigned int64). This helps handling number objects e.g. comparing two numbers. number(double value) : m_value(value), m_type(double_type) {} number(int32_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {} number(uint32_t value) : m_intval(value), m_type(unsigned_type) {} number(int64_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {} number(uint64_t value) : m_uintval(value), m_type(unsigned_type) {} public: /// /// Does the number fit into int32? /// /// true if the number fits into int32, false otherwise _ASYNCRTIMP bool is_int32() const; /// /// Does the number fit into unsigned int32? /// /// true if the number fits into unsigned int32, false otherwise _ASYNCRTIMP bool is_uint32() const; /// /// Does the number fit into int64? /// /// true if the number fits into int64, false otherwise _ASYNCRTIMP bool is_int64() const; /// /// Does the number fit into unsigned int64? /// /// true if the number fits into unsigned int64, false otherwise bool is_uint64() const { switch (m_type) { case signed_type: return m_intval >= 0; case unsigned_type: return true; case double_type: default: return false; } } /// /// Converts the JSON number to a C++ double. /// /// A double representation of the number double to_double() const { switch (m_type) { case double_type: return m_value; case signed_type: return static_cast(m_intval); case unsigned_type: return static_cast(m_uintval); default: return false; } } /// /// Converts the JSON number to int32. /// /// An int32 representation of the number int32_t to_int32() const { if (m_type == double_type) return static_cast(m_value); else return static_cast(m_intval); } /// /// Converts the JSON number to unsigned int32. /// /// An unsigned int32 representation of the number uint32_t to_uint32() const { if (m_type == double_type) return static_cast(m_value); else return static_cast(m_intval); } /// /// Converts the JSON number to int64. /// /// An int64 representation of the number int64_t to_int64() const { if (m_type == double_type) return static_cast(m_value); else return static_cast(m_intval); } /// /// Converts the JSON number to unsigned int64. /// /// An unsigned int64 representation of the number uint64_t to_uint64() const { if (m_type == double_type) return static_cast(m_value); else return static_cast(m_intval); } /// /// Is the number represented internally as an integral type? /// /// true if the number is represented as an integral type, false otherwise bool is_integral() const { return m_type != double_type; } /// /// Compares two JSON numbers for equality. /// /// The JSON number to compare with. /// True if the numbers are equal. bool operator==(const number& other) const { if (m_type != other.m_type) return false; switch (m_type) { case json::number::type::signed_type: return m_intval == other.m_intval; case json::number::type::unsigned_type: return m_uintval == other.m_uintval; case json::number::type::double_type: return m_value == other.m_value; } __assume(0); // Absence of this return statement provokes a warning from Intel // compiler, but its presence results in a warning from MSVC, so // we have to resort to conditional compilation to keep both happy. #ifdef __INTEL_COMPILER return false; #endif } private: union { int64_t m_intval; uint64_t m_uintval; double m_value; }; enum type { signed_type = 0, unsigned_type, double_type } m_type; friend class details::_Number; }; namespace details { class _Value { public: virtual std::unique_ptr<_Value> _copy_value() = 0; virtual bool has_field(const utility::string_t&) const { return false; } virtual value get_field(const utility::string_t&) const { throw json_exception("not an object"); } virtual value get_element(array::size_type) const { throw json_exception("not an array"); } virtual value& index(const utility::string_t&) { throw json_exception("not an object"); } virtual value& index(array::size_type) { throw json_exception("not an array"); } virtual const value& cnst_index(const utility::string_t&) const { throw json_exception("not an object"); } virtual const value& cnst_index(array::size_type) const { throw json_exception("not an array"); } // Common function used for serialization to strings and streams. virtual void serialize_impl(std::string& str) const { format(str); } #ifdef _WIN32 virtual void serialize_impl(std::wstring& str) const { format(str); } #endif virtual utility::string_t to_string() const { utility::string_t str; serialize_impl(str); return str; } virtual json::value::value_type type() const { return json::value::Null; } virtual bool is_integer() const { throw json_exception("not a number"); } virtual bool is_double() const { throw json_exception("not a number"); } virtual const json::number& as_number() { throw json_exception("not a number"); } virtual double as_double() const { throw json_exception("not a number"); } virtual int as_integer() const { throw json_exception("not a number"); } virtual bool as_bool() const { throw json_exception("not a boolean"); } virtual json::array& as_array() { throw json_exception("not an array"); } virtual const json::array& as_array() const { throw json_exception("not an array"); } virtual json::object& as_object() { throw json_exception("not an object"); } virtual const json::object& as_object() const { throw json_exception("not an object"); } virtual const utility::string_t& as_string() const { throw json_exception("not a string"); } virtual size_t size() const { return 0; } virtual ~_Value() {} protected: _Value() {} virtual void format(std::basic_string& stream) const { stream.append("null"); } #ifdef _WIN32 virtual void format(std::basic_string& stream) const { stream.append(L"null"); } #endif private: friend class web::json::value; }; class _Null : public _Value { public: virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Null>(); } virtual json::value::value_type type() const { return json::value::Null; } }; class _Number : public _Value { public: _Number(double value) : m_number(value) {} _Number(int32_t value) : m_number(value) {} _Number(uint32_t value) : m_number(value) {} _Number(int64_t value) : m_number(value) {} _Number(uint64_t value) : m_number(value) {} virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Number>(*this); } virtual json::value::value_type type() const { return json::value::Number; } virtual bool is_integer() const { return m_number.is_integral(); } virtual bool is_double() const { return !m_number.is_integral(); } virtual double as_double() const { return m_number.to_double(); } virtual int as_integer() const { return m_number.to_int32(); } virtual const number& as_number() { return m_number; } protected: virtual void format(std::basic_string& stream) const; #ifdef _WIN32 virtual void format(std::basic_string& stream) const; #endif private: template friend class json::details::JSON_Parser; json::number m_number; }; class _Boolean : public _Value { public: _Boolean(bool value) : m_value(value) {} virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Boolean>(*this); } virtual json::value::value_type type() const { return json::value::Boolean; } virtual bool as_bool() const { return m_value; } protected: virtual void format(std::basic_string& stream) const { stream.append(m_value ? "true" : "false"); } #ifdef _WIN32 virtual void format(std::basic_string& stream) const { stream.append(m_value ? L"true" : L"false"); } #endif private: template friend class json::details::JSON_Parser; bool m_value; }; class _String : public _Value { public: _String(utility::string_t value) : m_string(std::move(value)) { m_has_escape_char = has_escape_chars(*this); } _String(utility::string_t value, bool escaped_chars) : m_string(std::move(value)), m_has_escape_char(escaped_chars) { } #ifdef _WIN32 _String(std::string&& value) : m_string(utility::conversions::to_utf16string(std::move(value))) { m_has_escape_char = has_escape_chars(*this); } _String(std::string&& value, bool escape_chars) : m_string(utility::conversions::to_utf16string(std::move(value))), m_has_escape_char(escape_chars) { } #endif virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_String>(*this); } virtual json::value::value_type type() const { return json::value::String; } virtual const utility::string_t& as_string() const; virtual void serialize_impl(std::string& str) const { serialize_impl_char_type(str); } #ifdef _WIN32 virtual void serialize_impl(std::wstring& str) const { serialize_impl_char_type(str); } #endif protected: virtual void format(std::basic_string& str) const; #ifdef _WIN32 virtual void format(std::basic_string& str) const; #endif private: friend class _Object; friend class _Array; size_t get_reserve_size() const { return m_string.size() + 2; } template void serialize_impl_char_type(std::basic_string& str) const { // To avoid repeated allocations reserve some space all up front. // size of string + 2 for quotes str.reserve(get_reserve_size()); format(str); } std::string as_utf8_string() const; utf16string as_utf16_string() const; utility::string_t m_string; // There are significant performance gains that can be made by knowing whether // or not a character that requires escaping is present. bool m_has_escape_char; static bool has_escape_chars(const _String& str); }; template _ASYNCRTIMP void append_escape_string(std::basic_string& str, const std::basic_string& escaped); void format_string(const utility::string_t& key, utility::string_t& str); #ifdef _WIN32 void format_string(const utility::string_t& key, std::string& str); #endif class _Object : public _Value { public: _Object(bool keep_order) : m_object(keep_order) {} _Object(object::storage_type fields, bool keep_order) : m_object(std::move(fields), keep_order) {} virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Object>(*this); } virtual json::object& as_object() { return m_object; } virtual const json::object& as_object() const { return m_object; } virtual json::value::value_type type() const { return json::value::Object; } virtual bool has_field(const utility::string_t&) const; virtual json::value& index(const utility::string_t& key); bool is_equal(const _Object* other) const { if (m_object.size() != other->m_object.size()) return false; return std::equal(std::begin(m_object), std::end(m_object), std::begin(other->m_object)); } virtual void serialize_impl(std::string& str) const { // To avoid repeated allocations reserve some space all up front. str.reserve(get_reserve_size()); format(str); } #ifdef _WIN32 virtual void serialize_impl(std::wstring& str) const { // To avoid repeated allocations reserve some space all up front. str.reserve(get_reserve_size()); format(str); } #endif size_t size() const { return m_object.size(); } protected: virtual void format(std::basic_string& str) const { format_impl(str); } #ifdef _WIN32 virtual void format(std::basic_string& str) const { format_impl(str); } #endif private: json::object m_object; template friend class json::details::JSON_Parser; template void format_impl(std::basic_string& str) const { str.push_back('{'); if (!m_object.empty()) { auto lastElement = m_object.end() - 1; for (auto iter = m_object.begin(); iter != lastElement; ++iter) { format_string(iter->first, str); str.push_back(':'); iter->second.format(str); str.push_back(','); } format_string(lastElement->first, str); str.push_back(':'); lastElement->second.format(str); } str.push_back('}'); } size_t get_reserve_size() const { // This is a heuristic we can tune more in the future: // Basically size of string plus // sum size of value if an object, array, or string. size_t reserveSize = 2; // For brackets {} for (auto iter = m_object.begin(); iter != m_object.end(); ++iter) { reserveSize += iter->first.length() + 2; // 2 for quotes size_t valueSize = iter->second.size() * 20; // Multiply by each object/array element if (valueSize == 0) { if (iter->second.type() == json::value::String) { valueSize = static_cast<_String*>(iter->second.m_value.get())->get_reserve_size(); } else { valueSize = 5; // true, false, or null } } reserveSize += valueSize; } return reserveSize; } }; class _Array : public _Value { public: _Array() {} _Array(array::size_type size) : m_array(size) {} _Array(array::storage_type elements) : m_array(std::move(elements)) {} virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Array>(*this); } virtual json::value::value_type type() const { return json::value::Array; } virtual json::array& as_array() { return m_array; } virtual const json::array& as_array() const { return m_array; } virtual json::value& index(json::array::size_type index) { return m_array[index]; } bool is_equal(const _Array* other) const { if (m_array.size() != other->m_array.size()) return false; auto iterT = m_array.cbegin(); auto iterO = other->m_array.cbegin(); auto iterTe = m_array.cend(); auto iterOe = other->m_array.cend(); for (; iterT != iterTe && iterO != iterOe; ++iterT, ++iterO) { if (*iterT != *iterO) return false; } return true; } virtual void serialize_impl(std::string& str) const { // To avoid repeated allocations reserve some space all up front. str.reserve(get_reserve_size()); format(str); } #ifdef _WIN32 virtual void serialize_impl(std::wstring& str) const { // To avoid repeated allocations reserve some space all up front. str.reserve(get_reserve_size()); format(str); } #endif size_t size() const { return m_array.size(); } protected: virtual void format(std::basic_string& str) const { format_impl(str); } #ifdef _WIN32 virtual void format(std::basic_string& str) const { format_impl(str); } #endif private: json::array m_array; template friend class json::details::JSON_Parser; template void format_impl(std::basic_string& str) const { str.push_back('['); if (!m_array.m_elements.empty()) { auto lastElement = m_array.m_elements.end() - 1; for (auto iter = m_array.m_elements.begin(); iter != lastElement; ++iter) { iter->format(str); str.push_back(','); } lastElement->format(str); } str.push_back(']'); } size_t get_reserve_size() const { // This is a heuristic we can tune more in the future: // Basically sum size of each value if an object, array, or string by a multiplier. size_t reserveSize = 2; // For brackets [] for (auto iter = m_array.cbegin(); iter != m_array.cend(); ++iter) { size_t valueSize = iter->size() * 20; // Per each nested array/object if (valueSize == 0) valueSize = 5; // true, false, or null reserveSize += valueSize; } return reserveSize; } }; } // namespace details /// /// Gets the number of children of the value. /// /// The number of children. 0 for all non-composites. inline size_t json::value::size() const { return m_value->size(); } /// /// Test for the presence of a field. /// /// The name of the field /// True if the field exists, false otherwise. inline bool json::value::has_field(const utility::string_t& key) const { return m_value->has_field(key); } /// /// Access a field of a JSON object. /// /// The name of the field /// The value kept in the field; null if the field does not exist inline json::value json::value::get(const utility::string_t& key) const { return m_value->get_field(key); } /// /// Access an element of a JSON array. /// /// The index of an element in the JSON array /// The value kept at the array index; null if outside the boundaries of the array inline json::value json::value::get(size_t index) const { return m_value->get_element(index); } /// /// A standard std::ostream operator to facilitate writing JSON values to streams. /// /// The output stream to write the JSON value to. /// The JSON value to be written to the stream. /// The output stream object _ASYNCRTIMP utility::ostream_t& __cdecl operator<<(utility::ostream_t& os, const json::value& val); /// /// A standard std::istream operator to facilitate reading JSON values from streams. /// /// The input stream to read the JSON value from. /// The JSON value object read from the stream. /// The input stream object. _ASYNCRTIMP utility::istream_t& __cdecl operator>>(utility::istream_t& is, json::value& val); } // namespace json } // namespace web #endif