Documentation work
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
/// 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>
|
||||
@@ -7,161 +14,166 @@
|
||||
|
||||
// TODO: Implement standard iterators for json::object_val and json::array_val
|
||||
|
||||
|
||||
/// Redacted Software JSON API - Parse, Serialize, Validate, and Build JSON files.
|
||||
namespace JJX::json {
|
||||
enum class token_type { string, number, syntax, boolean, null };
|
||||
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;
|
||||
};
|
||||
|
||||
struct string;
|
||||
struct number;
|
||||
struct boolean;
|
||||
struct object;
|
||||
struct array;
|
||||
/// 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 };
|
||||
|
||||
struct value {
|
||||
|
||||
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;
|
||||
|
||||
value_type type;
|
||||
/// 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};
|
||||
|
||||
|
||||
explicit value() : type(value_type::null) { }
|
||||
explicit value(double v) : type(value_type::number), number(v) {}
|
||||
explicit value(const std::string& v) : type(value_type::string), string(v) {}
|
||||
struct token {
|
||||
std::string value;
|
||||
token_type type;
|
||||
int location;
|
||||
std::shared_ptr<std::string> full_source;
|
||||
};
|
||||
|
||||
//explicit value(bool v) : type(value_type::boolean), boolean(v) {}
|
||||
explicit value(const std::vector<value>& v) : type(value_type::array), array(v) {}
|
||||
explicit value(const std::map<std::string, value>& v) : type(value_type::object), object(v) {}
|
||||
explicit value(const std::pair<std::string, value>& kvp) : type(value_type::object), object({kvp}) {}
|
||||
explicit value(const std::vector<std::pair<std::string, value>>& kvp_list) : type(value_type::object)
|
||||
{
|
||||
for (auto& kvp : kvp_list)
|
||||
object->insert(kvp);
|
||||
/// 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 = "");
|
||||
|
||||
|
||||
struct value
|
||||
{
|
||||
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;
|
||||
|
||||
value_type type;
|
||||
|
||||
/// The default constructor initializes to a JSON null.
|
||||
explicit value();
|
||||
/// Constructs a json value that represents the corresponding number.
|
||||
explicit value(double v);
|
||||
explicit value(const std::string& v);
|
||||
explicit value(const std::vector<value>& v);
|
||||
explicit value(const std::map<std::string, value>& v);
|
||||
explicit value(const std::pair<std::string, value>& kvp) : object({kvp}), type(value_type::object) {}
|
||||
explicit value(const std::vector<std::pair<std::string, value>>& kvp_list);
|
||||
|
||||
|
||||
value& operator=(double in);
|
||||
value& operator=(const std::string& in);
|
||||
value& operator=(bool in);
|
||||
value& operator=(const std::vector<value>& in);
|
||||
value& operator=(const std::map<std::string, value>& in);
|
||||
|
||||
explicit operator double() const { return number.value(); }
|
||||
explicit operator std::string() const { return string.value(); }
|
||||
explicit operator bool() const { return boolean.value(); }
|
||||
explicit operator std::vector<value>() const { return this->value::array.value(); }
|
||||
explicit operator std::map<std::string, value>() const { return object.value(); }
|
||||
|
||||
[[nodiscard]] struct object as_object() const;
|
||||
[[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();
|
||||
|
||||
};
|
||||
|
||||
struct string : value {
|
||||
explicit string() : value("") {}
|
||||
explicit string(const std::string& v) : value(v) {}
|
||||
|
||||
|
||||
operator std::string();
|
||||
};
|
||||
struct number : value {
|
||||
explicit number() : value(0.0) {}
|
||||
explicit number(double v) : value(v) {}
|
||||
};
|
||||
struct boolean : value {
|
||||
explicit boolean() : value(false) {}
|
||||
explicit boolean(bool v) : value(v) {}
|
||||
};
|
||||
|
||||
struct object : value
|
||||
{
|
||||
explicit object() : value(std::map<std::string, value>{}) {}
|
||||
explicit object(const std::map<std::string, value>& v) : value(v) {}
|
||||
|
||||
void insert(const std::pair<std::string, value>& kvp);
|
||||
|
||||
void insert(const std::string& key, const value& val);
|
||||
|
||||
|
||||
json::object& operator+=(const std::pair<std::string, value>& kvp);
|
||||
|
||||
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);
|
||||
|
||||
value& at(const std::string& key);
|
||||
value& operator[] (const std::string& key);
|
||||
|
||||
bool contains(const std::string& key);
|
||||
|
||||
json::value_type type_of(const std::string& key);
|
||||
};
|
||||
|
||||
struct array : value {
|
||||
explicit array() : value(std::vector<value>{}) {}
|
||||
explicit array(const std::vector<value>& v) : value(v) {}
|
||||
explicit array(const value& v) {
|
||||
if (v.type == value_type::array) {
|
||||
this->value::array = v.value::array;
|
||||
}
|
||||
}
|
||||
|
||||
explicit array(const array& v) = default;
|
||||
|
||||
value& operator=(double in);
|
||||
value& operator=(const std::string& in);
|
||||
value& operator=(bool in);
|
||||
value& operator=(const std::vector<value>& in);
|
||||
value& operator=(const std::map<std::string, value>& in);
|
||||
void push_back(const value& element);
|
||||
|
||||
explicit operator double() const { return number.value(); }
|
||||
explicit operator std::string() const { return string.value(); }
|
||||
explicit operator bool() const { return boolean.value(); }
|
||||
explicit operator std::vector<value>() const { return this->value::array.value(); }
|
||||
explicit operator std::map<std::string, value>() const { return object.value(); }
|
||||
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; }
|
||||
|
||||
[[nodiscard]] struct object as_object() const;
|
||||
[[nodiscard]] struct array as_array() const;
|
||||
[[nodiscard]] struct string as_string() const;
|
||||
[[nodiscard]] struct number as_number() const;
|
||||
[[nodiscard]] struct boolean as_boolean() const;
|
||||
value& operator[] (int key);
|
||||
|
||||
json::value_type type_of(int index);
|
||||
|
||||
/// Converts all JSON values contained in this object / array to the derived json value types.
|
||||
void convert_descendants();
|
||||
|
||||
};
|
||||
|
||||
struct string : value {
|
||||
explicit string() : value("") {}
|
||||
explicit string(const std::string& v) : value(v) {}
|
||||
|
||||
|
||||
operator std::string();
|
||||
};
|
||||
struct number : value {
|
||||
explicit number() : value(0.0) {}
|
||||
explicit number(double v) : value(v) {}
|
||||
};
|
||||
struct boolean : value {
|
||||
explicit boolean() : value(false) {}
|
||||
explicit boolean(bool v) : value(v) {}
|
||||
};
|
||||
struct object : value
|
||||
{
|
||||
explicit object() : value(std::map<std::string, value>{}) {}
|
||||
explicit object(const std::map<std::string, value>& v) : value(v) {}
|
||||
|
||||
|
||||
void insert(const std::pair<std::string, value>& kvp)
|
||||
{
|
||||
value::object->insert(kvp);
|
||||
}
|
||||
|
||||
void insert(const std::string& key, const value& val)
|
||||
{
|
||||
value::object->insert({key, val});
|
||||
}
|
||||
|
||||
|
||||
json::object& operator+=(const std::pair<std::string, value>& kvp) {
|
||||
insert(kvp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
json::object& operator+=(const std::pair<std::string, bool>& kvp) {
|
||||
insert({kvp.first, json::boolean(kvp.second)});
|
||||
return *this;
|
||||
}
|
||||
|
||||
json::object& operator+=(const std::pair<std::string, double>& kvp) {
|
||||
insert({kvp.first, json::number(kvp.second)});
|
||||
return *this;
|
||||
}
|
||||
|
||||
json::object& operator+=(const std::pair<std::string, std::string>& kvp) {
|
||||
insert({kvp.first, json::string(kvp.second)});
|
||||
return *this;
|
||||
}
|
||||
|
||||
value& at(const std::string& key);
|
||||
value& operator[] (const std::string& key);
|
||||
|
||||
bool contains(const std::string& key);
|
||||
|
||||
json::value_type type_of(const std::string& key);
|
||||
|
||||
|
||||
|
||||
};
|
||||
struct array : value {
|
||||
explicit array() : value(std::vector<value>{}) {}
|
||||
explicit array(const std::vector<value>& v) : value(v) {}
|
||||
explicit array(const value& v) {
|
||||
if (v.type == value_type::array)
|
||||
{
|
||||
this->value::array = v.value::array;
|
||||
}
|
||||
}
|
||||
explicit array(const array& v) = default;
|
||||
|
||||
void push_back(const value& element);
|
||||
|
||||
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;
|
||||
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(); }
|
||||
@@ -173,9 +185,6 @@ namespace JJX::json {
|
||||
[[nodiscard]] const_iterator cend() const;
|
||||
};
|
||||
|
||||
std::tuple<std::vector<json::token>, std::string> lex(std::string);
|
||||
std::tuple<json::value, int, std::string> parse(std::vector<json::token>, int index = 0);
|
||||
std::tuple<json::value, std::string> parse(std::string);
|
||||
std::string deparse(json::value, std::string whitespace = "");
|
||||
|
||||
|
||||
}
|
||||
|
16
main.cpp
16
main.cpp
@@ -75,9 +75,9 @@ struct product_info
|
||||
std::string review_text;
|
||||
std::string user;
|
||||
|
||||
operator json::object_val() const
|
||||
operator json::object() const
|
||||
{
|
||||
json::object_val obj = json::object();
|
||||
json::object obj = json::object();
|
||||
obj["rating"] = json::number(rating);
|
||||
obj["review_text"] = json::string(review_text);
|
||||
obj["user"] = json::string(user);
|
||||
@@ -107,11 +107,11 @@ product_info pinfo_fromjson(json::value root)
|
||||
|
||||
auto better_root = root.as_object();
|
||||
|
||||
this_product.category = better_root["category"];
|
||||
this_product.description = better_root["description"];
|
||||
this_product.category = better_root["category"].string.value();
|
||||
this_product.description = better_root["description"].string.value();
|
||||
this_product.is_available = bool(better_root["is_available"]);
|
||||
this_product.manufacturer = better_root["manufacturer"];
|
||||
this_product.name = better_root["name"];
|
||||
this_product.manufacturer = better_root["manufacturer"].string.value();
|
||||
this_product.name = better_root["name"].string.value();
|
||||
this_product.price = double(better_root["price"]);
|
||||
|
||||
json::array subobj(better_root["reviews"]);
|
||||
@@ -122,8 +122,8 @@ product_info pinfo_fromjson(json::value root)
|
||||
json::object review_data = review_data_pre.as_object();
|
||||
product_info::review going_in;
|
||||
going_in.rating = double(review_data["rating"]);
|
||||
going_in.user = review_data["user"];
|
||||
going_in.review_text = review_data["review_text"];
|
||||
going_in.user = review_data["user"].string.value();
|
||||
going_in.review_text = review_data["review_text"].string.value();
|
||||
this_product.reviews.push_back(going_in);
|
||||
}
|
||||
|
||||
|
43
src/JSON.cpp
43
src/JSON.cpp
@@ -173,6 +173,11 @@ namespace JJX::json {
|
||||
return lex_keyword(raw_json, "false", token_type::boolean, index);
|
||||
}
|
||||
|
||||
value::value(const std::vector<std::pair<std::string, value>> &kvp_list): type(value_type::object) {
|
||||
for (auto& kvp : kvp_list)
|
||||
object->insert(kvp);
|
||||
}
|
||||
|
||||
json::value& value::operator=(double in)
|
||||
{
|
||||
type = value_type::number;
|
||||
@@ -544,6 +549,16 @@ namespace JJX::json {
|
||||
}
|
||||
}
|
||||
|
||||
value::value(): type(value_type::null) { }
|
||||
|
||||
value::value(double v): type(value_type::number), number(v) {}
|
||||
|
||||
value::value(const std::string &v): string(v), type(value_type::string) {}
|
||||
|
||||
value::value(const std::vector<value> &v): array(v), type(value_type::array) {}
|
||||
|
||||
value::value(const std::map<std::string, value> &v): object(v), type(value_type::object) {}
|
||||
|
||||
struct string string(const std::string &text) {
|
||||
struct string out;
|
||||
out.type = value_type::string;
|
||||
@@ -607,6 +622,34 @@ namespace JJX::json {
|
||||
return this->value::object->contains(key);
|
||||
}
|
||||
|
||||
void object::insert(const std::pair<std::string, value> &kvp) {
|
||||
value::object->insert(kvp);
|
||||
}
|
||||
|
||||
void object::insert(const std::string &key, const value &val) {
|
||||
value::object->insert({key, val});
|
||||
}
|
||||
|
||||
json::object & object::operator+=(const std::pair<std::string, value> &kvp) {
|
||||
insert(kvp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
json::object & object::operator+=(const std::pair<std::string, bool> &kvp) {
|
||||
insert({kvp.first, json::boolean(kvp.second)});
|
||||
return *this;
|
||||
}
|
||||
|
||||
json::object & object::operator+=(const std::pair<std::string, double> &kvp) {
|
||||
insert({kvp.first, json::number(kvp.second)});
|
||||
return *this;
|
||||
}
|
||||
|
||||
json::object & object::operator+=(const std::pair<std::string, std::string> &kvp) {
|
||||
insert({kvp.first, json::string(kvp.second)});
|
||||
return *this;
|
||||
}
|
||||
|
||||
value &object::at(const std::string &key) {
|
||||
return this->value::object.value()[key];
|
||||
}
|
||||
|
Reference in New Issue
Block a user