Work in progress refactor.
This commit is contained in:
@@ -2,5 +2,23 @@
|
||||
|
||||
namespace json
|
||||
{
|
||||
class file_error : public std::runtime_error
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
class io_error : public std::runtime_error
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
class parse_error : public std::runtime_error
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
class json_runtime_error : public std::runtime_error
|
||||
{
|
||||
|
||||
};
|
||||
}
|
111
include/json.hpp
111
include/json.hpp
@@ -84,24 +84,29 @@ namespace json {
|
||||
std::optional<std::vector<value>> array;
|
||||
std::optional<std::map<std::string, value>> object;
|
||||
|
||||
/// The default constructor initializes to a JSON null value.
|
||||
/// Constructs a JSON value that represents null, aka an empty field.
|
||||
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);
|
||||
explicit value(double v);
|
||||
/// Constructs a JSON value that represents the corresponding number.
|
||||
/// @note JSON has a unified number type, so this integer will be stored as a double.
|
||||
explicit value(int v);
|
||||
|
||||
value(bool v);
|
||||
/// Constructs a JSON value that represents the corresponding number.
|
||||
/// @note JSON has a unified number type, so this float will be stored as a double.
|
||||
explicit value(float v);
|
||||
|
||||
/// Constructs a json value that represents a string.
|
||||
explicit value(const std::string& v);
|
||||
|
||||
/// Constructs a JSON value that represents a boolean.
|
||||
explicit 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);
|
||||
@@ -114,6 +119,7 @@ namespace json {
|
||||
/// 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;
|
||||
@@ -122,12 +128,61 @@ namespace json {
|
||||
|
||||
/// 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;
|
||||
[[nodiscard]] struct object as_object_value() 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;
|
||||
[[nodiscard]] struct array as_array_value() const;
|
||||
/// Returns this value's data, converted to a json::string, which is a specialization of this type.
|
||||
[[nodiscard]] struct string as_string_value() const;
|
||||
/// Returns this value's data, converted to a json::number, which is a specialization of this type.
|
||||
[[nodiscard]] struct number as_number_value() const;
|
||||
/// Returns this value's data, converted to a json::boolean, which is a specialization of this type.
|
||||
[[nodiscard]] struct boolean as_boolean_value() const;
|
||||
|
||||
|
||||
/// Returns this value's object data as a standard map of JSON objects.
|
||||
std::map<std::string, value> as_object() const;
|
||||
|
||||
/// Returns this value's array data as a standard vector of JSON objects.
|
||||
std::vector<value> as_array() const;
|
||||
|
||||
|
||||
/// Returns this value's stored number in floating-point format.
|
||||
float as_float() const;
|
||||
|
||||
/// Returns this value's stored number in floating-point format.
|
||||
/// @param def A default value to return if this object does not contain a number.
|
||||
float as_float_or(float def) const;
|
||||
|
||||
double as_double() const;
|
||||
double as_double_or(double def) const;
|
||||
|
||||
int as_int() const;
|
||||
int as_int_or(int def) const;
|
||||
|
||||
|
||||
|
||||
std::string as_string() const;
|
||||
|
||||
std::string as_string_or(const std::string& def) const;
|
||||
|
||||
bool as_bool() const { return boolean.value(); }
|
||||
bool as_bool_or(bool def) const { return boolean.value_or(def);}
|
||||
|
||||
template <typename T>
|
||||
T as_or(T def) const;
|
||||
|
||||
|
||||
template<> float as_or(float def) const
|
||||
{ return number.value_or(def); }
|
||||
|
||||
template<> int as_or(int def) const
|
||||
{ return number.value_or(def); }
|
||||
|
||||
template<> double as_or(double def) const
|
||||
{ return number.value_or(def); }
|
||||
|
||||
template <typename T>
|
||||
T as() const;
|
||||
|
||||
/// Converts all JSON values contained in this object / array to the derived json value types.
|
||||
void convert_descendants();
|
||||
@@ -152,6 +207,13 @@ namespace json {
|
||||
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);
|
||||
|
||||
|
||||
bool is(value_type type) const
|
||||
{ return type == value_type::object; }
|
||||
|
||||
bool has_correct_value_type(value_type type) const
|
||||
{ }
|
||||
#pragma endregion
|
||||
};
|
||||
|
||||
@@ -168,6 +230,7 @@ namespace json {
|
||||
/// The default constructor initializes to floating-point literal zero.
|
||||
explicit number();
|
||||
explicit number(double v);
|
||||
|
||||
};
|
||||
struct boolean : value {
|
||||
/// The default constructor initializes to false.
|
||||
@@ -186,12 +249,30 @@ namespace json {
|
||||
|
||||
/// 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);
|
||||
void insert(const std::string& key, float value)
|
||||
{
|
||||
insert(key, json::number(value));
|
||||
}
|
||||
void insert(const std::string& key, int value)
|
||||
{
|
||||
insert(key, json::number(value));
|
||||
}
|
||||
void insert(const std::string& key, double value)
|
||||
{
|
||||
insert(key, json::number(value));
|
||||
}
|
||||
|
||||
void insert(const std::string& key, const std::string& value)
|
||||
{
|
||||
insert(key, json::string(value));
|
||||
}
|
||||
|
||||
/// Shorthand operator for adding a key-value pair to this object,
|
||||
json::object& operator+=(const std::pair<std::string, value>& kvp);
|
||||
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);
|
||||
|
||||
|
56
main.cpp
56
main.cpp
@@ -44,35 +44,21 @@ json::value vector3_to_json(const Vector3& v) {
|
||||
//auto x = json::number(input.x);
|
||||
//auto y = json::number(input.y);
|
||||
//auto z = json::number(input.z);
|
||||
return json::array({v.x, v.y, v.z});
|
||||
return json::array({json::number(v.x), json::number(v.y), json::number(v.z)});
|
||||
}
|
||||
|
||||
/// Creates Vector3 type out of json array object.
|
||||
Vector3 json_to_vector3(const json::value& obj) {
|
||||
Vector3 json_to_vector3(const json::value& val) {
|
||||
Vector3 value;
|
||||
auto arr = val.as_array();
|
||||
|
||||
value.x = obj.array->at(0).number.value_or(0);
|
||||
value.y = obj.array->at(1).number.value_or(0);
|
||||
value.z = obj.array->at(2).number.value_or(0);
|
||||
value.x = arr.at(0).as_float_or(0);
|
||||
value.y = arr.at(1).as_float_or(0);
|
||||
value.z = arr.at(2).as_float_or(0);
|
||||
|
||||
return {value};
|
||||
}
|
||||
|
||||
/// I personally recommend using the following signatures for serializing and deserializing your objects
|
||||
/// as this will be considered the standard interface for the library, going forward.
|
||||
/// It may end up being a template so don't worry so much about inheriting these structs, specifically.
|
||||
|
||||
struct json_serializable {
|
||||
// json::value serialize() const { ... }
|
||||
virtual json::value serialize() const = 0; //{ /* Create a json::value, add data, and return. */; };
|
||||
};
|
||||
|
||||
struct json_deserializable {
|
||||
explicit json_deserializable(const json::value&) = delete;
|
||||
};
|
||||
|
||||
struct json_convertible : json_serializable, json_deserializable {};
|
||||
|
||||
struct product_info
|
||||
{
|
||||
[[nodiscard]] json::value serialize() const;
|
||||
@@ -111,23 +97,25 @@ struct product_info
|
||||
|
||||
json::value product_info::serialize() const {
|
||||
json::object root;
|
||||
root += {"category", this->category};
|
||||
root += {"manufacturer", this->manufacturer};
|
||||
root += {"is_available", this->is_available};
|
||||
root += {"description", this->description};
|
||||
root += {"name", this->name};
|
||||
|
||||
root.insert("category", this->category);
|
||||
root += {"category", json::string( this->category)};
|
||||
root += {"manufacturer", json::string( this->manufacturer)};
|
||||
root += {"is_available", json::boolean(this->is_available)};
|
||||
root += {"description", json::string( this->description)};
|
||||
root += {"name", json::string( this->name)};
|
||||
|
||||
auto review_list = json::array();
|
||||
for (auto& rev : this->reviews)
|
||||
review_list += rev.serialize();
|
||||
|
||||
root["ratings"] = {
|
||||
{"average", this->ratings.average},
|
||||
{"total", this->ratings.total}
|
||||
};
|
||||
root["ratings"] = json::object({
|
||||
{"average", json::number(this->ratings.average)},
|
||||
{"total", json::number(this->ratings.total)}
|
||||
});
|
||||
|
||||
root += {"reviews", review_list};
|
||||
root += {"taters", 5.0};
|
||||
root += {"taters", json::number(5)};
|
||||
|
||||
return root;
|
||||
}
|
||||
@@ -138,10 +126,10 @@ product_info::product_info(const json::value &jv) {
|
||||
|
||||
auto jvo = jv.as_object();
|
||||
|
||||
this->category = jvo["category"].string.value();
|
||||
this->description = jvo["description"].string.value();
|
||||
this->is_available = jvo["is_available"].boolean.value();
|
||||
this->manufacturer = jvo["manufacturer"].string.value();
|
||||
this->category = jvo["category"].as_string();
|
||||
this->description = jvo["description"].as_string();
|
||||
this->is_available = jvo["is_available"].as_bool();
|
||||
this->manufacturer = jvo["manufacturer"].as_string();
|
||||
this->name = jvo["name"].string.value();
|
||||
this->price = double(jvo["price"]);
|
||||
|
||||
|
65
src/json.cpp
65
src/json.cpp
@@ -5,25 +5,18 @@
|
||||
|
||||
namespace json {
|
||||
|
||||
class file_error : public std::runtime_error
|
||||
{
|
||||
|
||||
struct serializable {
|
||||
virtual json::value serialize() const = 0;
|
||||
};
|
||||
|
||||
class io_error : public std::runtime_error
|
||||
{
|
||||
|
||||
struct deserializable {
|
||||
virtual void deserialize(const json::value&) = 0;
|
||||
};
|
||||
|
||||
class parse_error : public std::runtime_error
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
class json_runtime_error : public std::runtime_error
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
std::string format_error(std::string base, std::string source, int index) {
|
||||
std::ostringstream s;
|
||||
@@ -119,26 +112,46 @@ namespace json {
|
||||
|
||||
value::operator std::map<std::string, value>() const { return object.value(); }
|
||||
|
||||
struct json::object json::value::as_object() const {
|
||||
struct json::object json::value::as_object_value() const {
|
||||
return json::object(object.value());
|
||||
}
|
||||
|
||||
struct json::array json::value::as_array() const {
|
||||
struct json::array json::value::as_array_value() const {
|
||||
return json::array(array.value());
|
||||
}
|
||||
|
||||
struct json::string json::value::as_string() const {
|
||||
struct json::string json::value::as_string_value() const {
|
||||
return json::string(string.value());
|
||||
}
|
||||
|
||||
struct json::number json::value::as_number() const {
|
||||
struct json::number json::value::as_number_value() const {
|
||||
return json::number(number.value());
|
||||
}
|
||||
|
||||
struct json::boolean json::value::as_boolean() const {
|
||||
struct json::boolean json::value::as_boolean_value() const {
|
||||
return json::boolean(boolean.value());
|
||||
}
|
||||
|
||||
std::map<std::string, value> value::as_object() const
|
||||
{
|
||||
return object.value();
|
||||
}
|
||||
|
||||
std::vector<value> value::as_array() const
|
||||
{ return array.value(); }
|
||||
|
||||
float value::as_float() const
|
||||
{ return number.value(); }
|
||||
|
||||
float value::as_float_or(float def) const
|
||||
{ return number.value_or(def); }
|
||||
|
||||
std::string value::as_string() const
|
||||
{ return string.value();}
|
||||
|
||||
std::string value::as_string_or(const std::string& def) const
|
||||
{ return string.value_or(def);}
|
||||
|
||||
void value::convert_descendants() {
|
||||
if (type == value_type::object)
|
||||
{
|
||||
@@ -146,27 +159,27 @@ namespace json {
|
||||
{
|
||||
if (v.type == value_type::object)
|
||||
{
|
||||
auto converted = v.as_object();
|
||||
auto converted = v.as_object_value();
|
||||
converted.convert_descendants();
|
||||
object.value().emplace(k, converted);
|
||||
}
|
||||
|
||||
if (v.type == value_type::array)
|
||||
{
|
||||
auto converted = v.as_array();
|
||||
auto converted = v.as_array_value();
|
||||
converted.convert_descendants();
|
||||
object.value().emplace(k, converted);
|
||||
}
|
||||
|
||||
|
||||
if (v.type == value_type::string)
|
||||
object.value().emplace(k, v.as_string());
|
||||
object.value().emplace(k, v.as_string_value());
|
||||
|
||||
if (v.type == value_type::number)
|
||||
object.value().emplace(k, v.as_number());
|
||||
object.value().emplace(k, v.as_number_value());
|
||||
|
||||
if (v.type == value_type::boolean)
|
||||
object.value().emplace(k, v.as_boolean());
|
||||
object.value().emplace(k, v.as_boolean_value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,19 +189,19 @@ namespace json {
|
||||
{
|
||||
auto v = array.value()[i];
|
||||
if (v.type == value_type::object) {
|
||||
auto converted = v.as_object();
|
||||
auto converted = v.as_object_value();
|
||||
converted.convert_descendants();
|
||||
array.value()[i] = converted;
|
||||
} else if (v.type == value_type::array) {
|
||||
auto converted = v.as_array();
|
||||
auto converted = v.as_array_value();
|
||||
converted.convert_descendants();
|
||||
array.value()[i] = converted;
|
||||
} else if (v.type == value_type::string)
|
||||
array.value()[i] = v.as_string();
|
||||
array.value()[i] = v.as_string_value();
|
||||
else if (v.type == value_type::number)
|
||||
array.value()[i] = v.as_number();
|
||||
array.value()[i] = v.as_number_value();
|
||||
else if (v.type == value_type::boolean)
|
||||
array.value()[i] = v.as_boolean();
|
||||
array.value()[i] = v.as_boolean_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user