From ceaa676a3a83521ab12f2ff5e094eb01441865eb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Sep 2024 21:36:35 +0300 Subject: [PATCH] add dv::value support to json::stringify & add dv.cpp --- src/coders/json.cpp | 122 +++++++++++++++++++++++ src/coders/json.hpp | 5 + src/data/dv.cpp | 180 ++++++++++++++++++++++++++++++++++ src/data/dv.hpp | 227 +++++-------------------------------------- test/coders/json.cpp | 35 +++++++ 5 files changed, 368 insertions(+), 201 deletions(-) create mode 100644 src/data/dv.cpp diff --git a/src/coders/json.cpp b/src/coders/json.cpp index a15a40a2..aa8294b9 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -52,6 +52,22 @@ void stringifyArr( bool nice ); +void stringifyObj( + const dv::value& obj, + std::stringstream& ss, + int indent, + const std::string& indentstr, + bool nice +); + +void stringifyList( + const dv::value& list, + std::stringstream& ss, + int indent, + const std::string& indentstr, + bool nice +); + void stringifyValue( const Value& value, std::stringstream& ss, @@ -80,6 +96,46 @@ void stringifyValue( } } +void stringifyValue( + const dv::value& value, + std::stringstream& ss, + int indent, + const std::string& indentstr, + bool nice +) { + using dv::value_type; + + switch (value.getType()) { + case value_type::object: + stringifyObj(value, ss, indent, indentstr, nice); + break; + case value_type::list: + stringifyList(value, ss, indent, indentstr, nice); + break; + case value_type::bytes: { + const auto& bytes = value.asBytes(); + ss << "\"" << util::base64_encode(bytes.data(), bytes.size()); + ss << "\""; + break; + } + case value_type::string: + ss << util::escape(value.asString()); + break; + case value_type::number: + ss << std::setprecision(15) << value.asNumber(); + break; + case value_type::integer: + ss << value.asInteger(); + break; + case value_type::boolean: + ss << (value.asBoolean() ? "true" : "false"); + break; + case value_type::none: + ss << "null"; + break; + } +} + void stringifyArr( const List* list, std::stringstream& ss, @@ -148,6 +204,64 @@ void stringifyObj( ss << '}'; } +void stringifyList( + const dv::value& list, + std::stringstream& ss, + int indent, + const std::string& indentstr, + bool nice +) { + if (list.empty()) { + ss << "[]"; + return; + } + ss << "["; + for (size_t i = 0; i < list.size(); i++) { + if (i > 0 || nice) { + newline(ss, nice, indent, indentstr); + } + const auto& value = list[i]; + stringifyValue(value, ss, indent + 1, indentstr, nice); + if (i + 1 < list.size()) { + ss << ','; + } + } + if (nice) { + newline(ss, true, indent - 1, indentstr); + } + ss << ']'; +} + +void stringifyObj( + const dv::value& obj, + std::stringstream& ss, + int indent, + const std::string& indentstr, + bool nice +) { + if (obj.empty()) { + ss << "{}"; + return; + } + ss << "{"; + size_t index = 0; + for (auto& [key, value] : obj.asObject()) { + if (index > 0 || nice) { + newline(ss, nice, indent, indentstr); + } + ss << util::escape(key) << ": "; + stringifyValue(value, ss, indent + 1, indentstr, nice); + index++; + if (index < obj.size()) { + ss << ','; + } + } + if (nice) { + newline(ss, true, indent - 1, indentstr); + } + ss << '}'; +} + std::string json::stringify( const Map* obj, bool nice, const std::string& indent ) { @@ -172,6 +286,14 @@ std::string json::stringify( return ss.str(); } +std::string json::stringifyDV( + const dv::value& value, bool nice, const std::string& indent +) { + std::stringstream ss; + stringifyValue(value, ss, 1, indent, nice); + return ss.str(); +} + Parser::Parser(std::string_view filename, std::string_view source) : BasicParser(filename, source) { } diff --git a/src/coders/json.hpp b/src/coders/json.hpp index 1491be70..45d1866f 100644 --- a/src/coders/json.hpp +++ b/src/coders/json.hpp @@ -3,6 +3,7 @@ #include #include "data/dynamic.hpp" +#include "data/dv.hpp" #include "typedefs.hpp" #include "binary_json.hpp" @@ -21,4 +22,8 @@ namespace json { std::string stringify( const dynamic::Value& value, bool nice, const std::string& indent ); + + std::string stringifyDV( + const dv::value& value, bool nice, const std::string& indent=" " + ); } diff --git a/src/data/dv.cpp b/src/data/dv.cpp new file mode 100644 index 00000000..cf334210 --- /dev/null +++ b/src/data/dv.cpp @@ -0,0 +1,180 @@ +#include "dv.hpp" + +#include "util/Buffer.hpp" + +namespace dv { + value::value(value_type type) : type(type) { + switch (type) { + case value_type::object: + val.object = std::make_shared(); + break; + case value_type::list: + val.list = std::make_shared(); + break; + case value_type::bytes: + val.bytes = nullptr; // no default size + break; + case value_type::string: + val.string = std::make_unique(""); + break; + default: + break; + } + } + + value& value::operator[](const key_t& key) { + if (type == value_type::object) { + return (*val.object)[key]; + } + throw std::runtime_error("value is not an object"); + } + const value& value::operator[](const key_t& key) const { + if (type == value_type::object) { + return (*val.object)[key]; + } + throw std::runtime_error("value is not an object"); + } + + value& value::operator=(const objects::Bytes& bytes) { + return setBytes(std::make_shared(bytes)); + } + + value& value::operator[](size_t index) { + if (type == value_type::list) { + return (*val.list)[index]; + } + throw std::runtime_error("value is not a list"); + } + const value& value::operator[](size_t index) const { + if (type == value_type::list) { + return (*val.list)[index]; + } + throw std::runtime_error("value is not a list"); + } + + value& value::add(value v) { + if (type == value_type::list) { + return val.list->add(std::move(v)); + } + throw std::runtime_error("value is not a list"); + } + + value& value::object(const key_t& key) { + reference ref = this->operator[](key); + ref = dv::object(); + return ref; + } + + value& value::list(const key_t& key) { + reference ref = this->operator[](key); + ref = dv::list(); + return ref; + } + + value& value::object() { + return add(dv::object()); + } + + value& value::list() { + return add(dv::list()); + } + + list_t::iterator value::begin() { + if (type == value_type::list) { + return val.list->begin(); + } + throw std::runtime_error("value is not a list"); + } + + list_t::iterator value::end() { + if (type == value_type::list) { + return val.list->end(); + } + throw std::runtime_error("value is not a list"); + } + + list_t::const_iterator value::begin() const { + if (type == value_type::list) { + const auto& constlist = *val.list; + return constlist.begin(); + } + throw std::runtime_error("value is not a list"); + } + + list_t::const_iterator value::end() const { + if (type == value_type::list) { + const auto& constlist = *val.list; + return constlist.end(); + } + throw std::runtime_error("value is not a list"); + } + + const std::string& value::asString() const { + if (type == value_type::string) { + return *val.string; + } + throw std::runtime_error("type error"); + } + + integer_t value::asInteger() const { + if (type == value_type::integer) { + return val.integer; + } else if (type == value_type::number) { + return static_cast(val.number); + } + throw std::runtime_error("type error"); + } + + number_t value::asNumber() const { + if (type == value_type::number) { + return val.number; + } else if (type == value_type::integer) { + return static_cast(val.integer); + } + throw std::runtime_error("type error"); + } + + boolean_t value::asBoolean() const { + if (type == value_type::boolean) { + return val.boolean; + } else if (type == value_type::integer) { + return val.integer != 0; + } + throw std::runtime_error("type error"); + } + + objects::Bytes& value::asBytes() { + if (type == value_type::bytes) { + return *val.bytes; + } + throw std::runtime_error("type error"); + } + + const objects::Bytes& value::asBytes() const { + if (type == value_type::bytes) { + return *val.bytes; + } + throw std::runtime_error("type error"); + } + + const objects::Object& value::asObject() const { + if (type == value_type::object) { + return *val.object; + } + throw std::runtime_error("type error"); + } + + const size_t value::size() const { + switch (type) { + case value_type::list: + return val.list->size(); + case value_type::object: + return val.object->size(); + case value_type::string: + return val.string->size(); + default: + throw std::runtime_error("type error"); + } + } +} + diff --git a/src/data/dv.hpp b/src/data/dv.hpp index e3c8d9e7..8dbeab24 100644 --- a/src/data/dv.hpp +++ b/src/data/dv.hpp @@ -168,58 +168,60 @@ namespace dv { } } - value& operator=(int8_t v) { + inline value& operator=(int8_t v) { return setInteger(v); } - value& operator=(int16_t v) { + inline value& operator=(int16_t v) { return setInteger(v); } - value& operator=(int32_t v) { + inline value& operator=(int32_t v) { return setInteger(v); } - value& operator=(int64_t v) { + inline value& operator=(int64_t v) { return setInteger(v); } - value& operator=(uint8_t v) { + inline value& operator=(uint8_t v) { return setInteger(v); } - value& operator=(uint16_t v) { + inline value& operator=(uint16_t v) { return setInteger(v); } - value& operator=(uint32_t v) { + inline value& operator=(uint32_t v) { return setInteger(v); } - value& operator=(uint64_t v) { + inline value& operator=(uint64_t v) { return setInteger(v); } - value& operator=(float v) { + inline value& operator=(float v) { return setNumber(v); } - value& operator=(double v) { + inline value& operator=(double v) { return setNumber(v); } - value& operator=(bool v) { + inline value& operator=(bool v) { return setBoolean(v); } - value& operator=(std::string_view v) { + inline value& operator=(std::string_view v) { return setString(std::string(v)); } - value& operator=(std::string v) { + inline value& operator=(std::string v) { return setString(std::move(v)); } - value& operator=(const char* v) { + inline value& operator=(const char* v) { return setString(v); } - value& operator=(std::shared_ptr ptr) { + inline value& operator=(std::shared_ptr ptr) { return setList(ptr); } - value& operator=(std::shared_ptr ptr) { + inline value& operator=(std::shared_ptr ptr) { return setObject(ptr); } - value& operator=(std::shared_ptr ptr) { + inline value& operator=(std::shared_ptr ptr) { return setBytes(ptr); } - value& operator=(const value& v) { + value& operator=(const objects::Bytes& bytes); + + inline value& operator=(const value& v) { switch (v.type) { case value_type::object: setObject(v.val.object); @@ -252,7 +254,7 @@ namespace dv { value& add(value v); template - value& add(T v) { + inline value& add(T v) { return add(value(v)); } @@ -292,7 +294,7 @@ namespace dv { const objects::Object& asObject() const; - value_type getType() const { + inline value_type getType() const { return type; } @@ -301,22 +303,15 @@ namespace dv { const size_t length() const { return size(); } - bool empty() const { + inline bool empty() const { return size() == 0; } }; using reference = value&; using const_reference = const value&; - - value list(); - value object(); - - value list(std::initializer_list values); } -#include "util/Buffer.hpp" - namespace dv::objects { class Object { map_t map; @@ -387,185 +382,15 @@ namespace dv::objects { } namespace dv { - value::value(value_type type) : type(type) { - switch (type) { - case value_type::object: - val.object = std::make_shared(); - break; - case value_type::list: - val.list = std::make_shared(); - break; - case value_type::bytes: - val.bytes = nullptr; // no default size - break; - case value_type::string: - val.string = std::make_unique(""); - break; - default: - break; - } - } - - value& value::operator[](const key_t& key) { - if (type == value_type::object) { - return (*val.object)[key]; - } - throw std::runtime_error("value is not an object"); - } - const value& value::operator[](const key_t& key) const { - if (type == value_type::object) { - return (*val.object)[key]; - } - throw std::runtime_error("value is not an object"); - } - - value& value::operator[](size_t index) { - if (type == value_type::list) { - return (*val.list)[index]; - } - throw std::runtime_error("value is not a list"); - } - const value& value::operator[](size_t index) const { - if (type == value_type::list) { - return (*val.list)[index]; - } - throw std::runtime_error("value is not a list"); - } - - value& value::add(value v) { - if (type == value_type::list) { - return val.list->add(std::move(v)); - } - throw std::runtime_error("value is not a list"); - } - - value& value::object(const key_t& key) { - reference ref = this->operator[](key); - ref = dv::object(); - return ref; - } - - value& value::list(const key_t& key) { - reference ref = this->operator[](key); - ref = dv::list(); - return ref; - } - - value& value::object() { - return add(dv::object()); - } - - value& value::list() { - return add(dv::list()); - } - - list_t::iterator value::begin() { - if (type == value_type::list) { - return val.list->begin(); - } - throw std::runtime_error("value is not a list"); - } - - list_t::iterator value::end() { - if (type == value_type::list) { - return val.list->end(); - } - throw std::runtime_error("value is not a list"); - } - - list_t::const_iterator value::begin() const { - if (type == value_type::list) { - const auto& constlist = *val.list; - return constlist.begin(); - } - throw std::runtime_error("value is not a list"); - } - - list_t::const_iterator value::end() const { - if (type == value_type::list) { - const auto& constlist = *val.list; - return constlist.end(); - } - throw std::runtime_error("value is not a list"); - } - - const std::string& value::asString() const { - if (type == value_type::string) { - return *val.string; - } - throw std::runtime_error("type error"); - } - - integer_t value::asInteger() const { - if (type == value_type::integer) { - return val.integer; - } else if (type == value_type::number) { - return static_cast(val.number); - } - throw std::runtime_error("type error"); - } - - number_t value::asNumber() const { - if (type == value_type::number) { - return val.number; - } else if (type == value_type::integer) { - return static_cast(val.integer); - } - throw std::runtime_error("type error"); - } - - boolean_t value::asBoolean() const { - if (type == value_type::boolean) { - return val.boolean; - } else if (type == value_type::integer) { - return val.integer != 0; - } - throw std::runtime_error("type error"); - } - - objects::Bytes& value::asBytes() { - if (type == value_type::bytes) { - return *val.bytes; - } - throw std::runtime_error("type error"); - } - - const objects::Bytes& value::asBytes() const { - if (type == value_type::bytes) { - return *val.bytes; - } - throw std::runtime_error("type error"); - } - - const objects::Object& value::asObject() const { - if (type == value_type::object) { - return *val.object; - } - throw std::runtime_error("type error"); - } - - const size_t value::size() const { - switch (type) { - case value_type::list: - return val.list->size(); - case value_type::object: - return val.object->size(); - case value_type::string: - return val.string->size(); - default: - throw std::runtime_error("type error"); - } - } - - value object() { + inline value object() { return std::make_shared(); } - value list() { + inline value list() { return std::make_shared(); } - value list(std::initializer_list values) { + inline value list(std::initializer_list values) { return std::make_shared(values); } } diff --git a/test/coders/json.cpp b/test/coders/json.cpp index 5738559b..c8c75c00 100644 --- a/test/coders/json.cpp +++ b/test/coders/json.cpp @@ -37,3 +37,38 @@ TEST(JSON, EncodeDecode) { } } } + +TEST(JSON, EncodeDecodeDV) { + const std::string name = "JSON-encoder"; + const int bytesSize = 20; + const int year = 2019; + const float score = 3.141592; + dynamic::ByteBuffer srcBytes(bytesSize); + for (int i = 0; i < bytesSize; i ++) { + srcBytes[i] = rand(); + } + + std::string text; + { + auto map = dv::object(); + map["name"] = name; + map["year"] = year; + map["score"] = score; + map["data"] = srcBytes; + + text = json::stringifyDV(map, false, ""); + } + { + auto map = json::parse(text); + EXPECT_EQ(map->get("name"), name); + EXPECT_EQ(map->get("year"), year); + EXPECT_FLOAT_EQ(map->get("score"), score); + auto b64string = map->get("data"); + + auto bytes = util::base64_decode(b64string); + EXPECT_EQ(bytes.size(), bytesSize); + for (int i = 0; i < bytesSize; i++) { + EXPECT_EQ(bytes[i], srcBytes[i]); + } + } +}