Documentation work

This commit is contained in:
2025-03-25 21:39:08 -05:00
parent 1253861ad1
commit 7bbcb98914
3 changed files with 205 additions and 153 deletions

View File

@@ -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 = "");
}

View File

@@ -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);
}

View File

@@ -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];
}