267 lines
11 KiB
C++
267 lines
11 KiB
C++
/// JJX - Josh's JSON and XML API for C++20
|
|
/// Written and maintained by josh@redacted.cc
|
|
/// Edit 3 - Revised March 24, 2025
|
|
/// (c) 2025 redacted.cc
|
|
/// This work is dedicated to the public domain.
|
|
|
|
|
|
#pragma once
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <cassert>
|
|
#include <vector>
|
|
|
|
// TODO: API compatibility with json::value and derived types.
|
|
// TODO: Auto-conversion from json::value *to* represented type.
|
|
|
|
/// Redacted Software JSON API - Parse, Serialize, Validate, and Build JSON files.
|
|
namespace json {
|
|
|
|
|
|
std::string format_error(std::string base, std::string source, int index);
|
|
|
|
/// An enumeration that represents the different types of tokens that exist.
|
|
/// A token is an atomic chunk of a JSON file's text, which is the smallest piece of syntax that can be considered in isolation.
|
|
/// Tokens may be JSON primitives such as string, number, boolean, null,
|
|
/// Or may be a syntax symbol, such as quotations, brackets, commas, etc.
|
|
enum class token_type { string, number, syntax, boolean, null };
|
|
|
|
/// An enumeration that represents the different types of JSON values that can exist.
|
|
/// JSON allows for two composite data types: arrays and objects.
|
|
/// Arrays are sequential lists of other JSON values, with integer indexing.
|
|
/// Objects are collections of named JSON values, with string indexing.
|
|
/// string, number, boolean, and null types are considered self-explanatory.
|
|
enum class value_type { string, number, object, array, boolean, null};
|
|
|
|
|
|
struct token {
|
|
std::string value;
|
|
token_type type;
|
|
int location;
|
|
std::shared_ptr<std::string> full_source;
|
|
};
|
|
|
|
/// The base class for a set of classes representing the various JSON primitives.
|
|
struct value;
|
|
struct string;
|
|
struct number;
|
|
struct boolean;
|
|
struct object;
|
|
struct array;
|
|
|
|
/// Tokenizes (or Lexes) a string of text into a list of JSON tokens.
|
|
/// @return Token list, and an optional error message.
|
|
/// @see struct token
|
|
std::tuple<std::vector<json::token>, std::string> lex(std::string);
|
|
|
|
/// Parses a sequence of JSON tokens.
|
|
/// @return The root JSON value, which will contain descendant JSON values if applicable,
|
|
/// an integer indicating the recursion index, and an error code, if applicable.
|
|
std::tuple<json::value, int, std::string> parse(std::vector<json::token>, int index = 0);
|
|
|
|
/// Parses a sequence of JSON tokens.
|
|
/// @return The root JSON value, which will contain descendant JSON values if applicable,
|
|
/// an integer indicating the recursion index, and an error code, if applicable.
|
|
std::tuple<json::value, std::string> parse(std::string);
|
|
std::string deparse(json::value, std::string whitespace = "");
|
|
|
|
/// This object holds JSON constructs, known as 'values', and associates them to a type.
|
|
/// A value represents a single piece of JSON information.
|
|
struct value
|
|
{
|
|
/// This member denotes what type of JSON value this object represents.
|
|
value_type type;
|
|
|
|
/// The following member variables store the actual data this object represents.
|
|
/// Only one member at a time should contain any data, depending on the value_type.
|
|
|
|
std::optional<std::string> string;
|
|
std::optional<double> number;
|
|
std::optional<bool> boolean;
|
|
std::optional<std::vector<value>> array;
|
|
std::optional<std::map<std::string, value>> object;
|
|
|
|
/// The default constructor initializes to a JSON null value.
|
|
explicit value();
|
|
/// Constructs a json value that represents the corresponding number.
|
|
value(double v);
|
|
value(int v);
|
|
value(float v);
|
|
/// Constructs a json value that represents a string.
|
|
value(const std::string& v);
|
|
|
|
value(bool v);
|
|
/// Constructs a json value that represents an array.
|
|
explicit value(const std::vector<value>& v);
|
|
/// Constructs a json value that represents an object.
|
|
explicit value(const std::map<std::string, value>& v);
|
|
/// Constructs a json value that represents an object, from a key-value pair.
|
|
explicit value(const std::pair<std::string, value>& kvp) : object({kvp}), type(value_type::object) {}
|
|
/// Constructs a json value that represents an object, from a list of key-value pairs.
|
|
//explicit value(const std::vector<std::pair<std::string, value>>& kvp_list);
|
|
|
|
/// Assigns this value to a number, and sets the value_type::number.
|
|
value& operator=(double in);
|
|
/// Assigns this value to a string, and sets the value_type::string.
|
|
value& operator=(const std::string& in);
|
|
/// Assigns this value to a boolean, and sets the value_type::boolean.
|
|
value& operator=(bool in);
|
|
/// Assigns this value to an array, and sets the value_type::array.
|
|
value& operator=(const std::vector<value>& in);
|
|
/// Assigns this value to a map, and sets the value_type::map.
|
|
value& operator=(const std::map<std::string, value>& in);
|
|
|
|
explicit operator double() const;
|
|
explicit operator std::string() const;
|
|
explicit operator bool() const;
|
|
explicit operator std::vector<value>() const;
|
|
explicit operator std::map<std::string, value>() const;
|
|
|
|
/// Returns this value's data, converted to a json::object, which is a specialization of this type.
|
|
/// @see struct object
|
|
[[nodiscard]] struct object as_object() const;
|
|
/// Returns this value's data, converted to a json::array, which is a specialization of this type.
|
|
[[nodiscard]] struct array as_array() const;
|
|
[[nodiscard]] struct string as_string() const;
|
|
[[nodiscard]] struct number as_number() const;
|
|
[[nodiscard]] struct boolean as_boolean() const;
|
|
|
|
/// Converts all JSON values contained in this object / array to the derived json value types.
|
|
void convert_descendants();
|
|
|
|
#pragma region Object Access Members
|
|
|
|
/// @return The json value associated with the given key.
|
|
/// @throws std::runtime_error if value_type != object
|
|
value& at(const std::string& key);
|
|
|
|
/// @throws std::runtime_error if value_type != array
|
|
value& at(int index);
|
|
/// @see object::at()
|
|
/// @throws std::runtime_error if value_type != object
|
|
value& operator[] (const std::string& key);
|
|
/// @throws std::runtime_error if value_type != object
|
|
const value& operator[] (const std::string& key) const;
|
|
|
|
/// @throws std::runtime_error if value_type != array
|
|
value& operator[] (int index);
|
|
/// @throws std::runtime_error if value_type != array
|
|
const value& operator[] (int index) const;
|
|
/// @return True if a key-value pair in this object has a key that matches the given value.
|
|
bool contains(const std::string& key);
|
|
#pragma endregion
|
|
};
|
|
|
|
/// A specialized json::value which provides STL-compatibility with std::string, and other functions for convenience.
|
|
struct string : value {
|
|
/// The default constructor initializes to an empty string literal.
|
|
explicit string() : value("") {}
|
|
explicit string(const std::string& v) : value(v) {}
|
|
|
|
|
|
operator std::string();
|
|
};
|
|
struct number : value {
|
|
/// The default constructor initializes to floating-point literal zero.
|
|
explicit number();
|
|
explicit number(double v);
|
|
};
|
|
struct boolean : value {
|
|
/// The default constructor initializes to false.
|
|
explicit boolean();
|
|
explicit boolean(bool v);
|
|
};
|
|
|
|
/// A specialized json::value which provides STL compatibility with std::map, and other functions for convenience.
|
|
/// As per JSON specification, only std::strings are allowed to be used as keys in this data type.
|
|
struct object : value
|
|
{
|
|
/// The default constructor initializes to an empty map.
|
|
explicit object();
|
|
/// Constructs this object from a list of key-value pairs, in the form of std::map.
|
|
explicit object(const std::map<std::string, value>& v);
|
|
|
|
/// Adds a key-value pair to this object.
|
|
void insert(const std::pair<std::string, value>& kvp);
|
|
/// Adds a key-value pair to this object.
|
|
/// @param key The key to associate with the value.
|
|
void insert(const std::string& key, const value& val);
|
|
|
|
/// Shorthand operator for adding a key-value pair to this object,
|
|
json::object& operator+=(const std::pair<std::string, value>& kvp);
|
|
/// Shorthand operator for adding a boolean-value to this object, with a string key.
|
|
/*json::object& operator+=(const std::pair<std::string, bool>& kvp);
|
|
|
|
json::object& operator+=(const std::pair<std::string, double>& kvp);
|
|
|
|
json::object& operator+=(const std::pair<std::string, std::string>& kvp);
|
|
*/
|
|
/// @return The json value associated with the given key.
|
|
value& at(const std::string& key);
|
|
|
|
/// @see object::at()
|
|
value& operator[] (const std::string& key);
|
|
const value& operator[] (const std::string& key) const;
|
|
|
|
/// @return True if a key-value pair in this object has a key that matches the given value.
|
|
bool contains(const std::string& key);
|
|
|
|
/// @return The json-type of the value of the first key-value pair where the key matches the given string.
|
|
json::value_type type_of(const std::string& key);
|
|
|
|
using map_spec = std::map<std::string, value>;
|
|
using iterator = map_spec::iterator;
|
|
using const_iterator = map_spec::const_iterator;
|
|
|
|
iterator begin() { return value::object->begin();}
|
|
iterator end() { return value::object->end();}
|
|
|
|
const_iterator begin() const { return value::object->cbegin(); }
|
|
const_iterator end() const { return value::object->cend(); }
|
|
|
|
size_t size() const { return value::object->size();}
|
|
bool empty() const { return value::object->empty(); }
|
|
|
|
};
|
|
|
|
struct array : value {
|
|
/// The default constructor
|
|
explicit array();
|
|
|
|
explicit array(const std::vector<value>& v);
|
|
|
|
/// Default Copy Constructor
|
|
explicit array(const array& v) = default;
|
|
|
|
void push_back(const value& element);
|
|
size_t size() const;
|
|
|
|
|
|
value& operator+= (const value& v);
|
|
|
|
value& operator+= (bool v) { push_back(json::boolean(v)); return *this; }
|
|
value& operator+= (double v) { push_back(json::number(v)); return *this; }
|
|
value& operator+= (const std::string& v) { push_back(json::string(v)); return *this; }
|
|
|
|
value& operator[] (int key);
|
|
|
|
json::value_type type_of(int index);
|
|
|
|
using iterator = std::vector<value>::iterator;
|
|
using const_iterator = std::vector<value>::const_iterator;
|
|
|
|
iterator begin() { return value::array->begin(); }
|
|
iterator end() { return value::array->end(); }
|
|
|
|
[[nodiscard]] const_iterator begin() const;
|
|
[[nodiscard]] const_iterator end() const;
|
|
|
|
[[nodiscard]] const_iterator cbegin() const;
|
|
[[nodiscard]] const_iterator cend() const;
|
|
};
|
|
|
|
}
|