From 40eb807882f6c9bdf7cec94c9499491d696c0d76 Mon Sep 17 00:00:00 2001 From: Sangwan Kwon Date: Thu, 14 May 2020 14:12:42 +0900 Subject: [PATCH] Add serialization to JSON Signed-off-by: Sangwan Kwon --- src/vist/common/tests/string.cpp | 20 +++++-- src/vist/json/array.hpp | 55 ++++++++++++------- src/vist/json/json.hpp | 52 +++++++++--------- src/vist/json/object.hpp | 90 +++++++++++++++++++++++++++----- src/vist/json/tests/json.cpp | 64 +++++++++++++++++++---- src/vist/json/value.hpp | 48 ++++++++++++++--- src/vist/string.hpp | 20 +++++-- 7 files changed, 266 insertions(+), 83 deletions(-) diff --git a/src/vist/common/tests/string.cpp b/src/vist/common/tests/string.cpp index 371d577..6cddefa 100644 --- a/src/vist/common/tests/string.cpp +++ b/src/vist/common/tests/string.cpp @@ -37,14 +37,13 @@ TEST(StringTests, rtrim) TEST(StringTests, trim) { std::string str = " a b c "; - trim(str); - EXPECT_EQ(str, "a b c"); + EXPECT_EQ(trim(str), "a b c"); } TEST(StringTests, split) { std::string origin = "a b c"; - auto token = split(origin, std::regex("\\s")); + auto token = split(origin, "\\s"); if (token.size() == 3) { EXPECT_EQ(token[0], "a"); EXPECT_EQ(token[1], "b"); @@ -54,7 +53,7 @@ TEST(StringTests, split) } origin = "a,b,c"; - token = split(origin, std::regex(",")); + token = split(origin, ","); EXPECT_EQ(token.size(), 3); if (token.size() == 3) { EXPECT_EQ(token[0], "a"); @@ -64,3 +63,16 @@ TEST(StringTests, split) EXPECT_TRUE(false); } } + +TEST(StringTests, strip) +{ + std::string origin = "{a b c}"; + EXPECT_EQ(strip(origin, '{', '}'), "a b c"); + + try { + strip(origin, '[', ']'); + EXPECT_TRUE(false); + } catch(...) { + EXPECT_TRUE(true); + } +} diff --git a/src/vist/json/array.hpp b/src/vist/json/array.hpp index 7f7083a..692e554 100644 --- a/src/vist/json/array.hpp +++ b/src/vist/json/array.hpp @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include @@ -25,9 +26,28 @@ namespace vist { namespace json { struct Array : public Value { - std::size_t size() const noexcept + template + void push(const Type& data) { - return this->buffer.size(); + auto value = std::make_shared(); + *value = data; + this->buffer.emplace_back(std::move(value)); + } + + Value& at(std::size_t index) + { + if (index > this->size()) + throw std::invalid_argument("Wrong index."); + + return *(this->buffer[index]); + } + + Value& operator[](std::size_t index) + { + if (index > this->size()) + throw std::invalid_argument("Wrong index."); + + return *(this->buffer[index]); } std::string serialize() const override @@ -49,28 +69,23 @@ struct Array : public Value { return ss.str(); } - template - void push(const Type& data) + void deserialize(const std::string& dumped) override { - auto value = std::make_shared(); - *value = data; - this->buffer.emplace_back(std::move(value)); - } - - Value& at(std::size_t index) - { - if (index > this->size()) - throw std::invalid_argument("Wrong index."); - - return *(this->buffer[index]); + if (dumped.empty()) + throw std::invalid_argument("Dumped value cannot empty."); + + auto stripped = strip(dumped, '[', ']'); + auto tokens = split(trim(stripped), ","); + for (const auto& token : tokens) { + auto value = std::make_shared(); + value->deserialize(trim(token)); + this->buffer.emplace_back(std::move(value)); + } } - Value& operator[](std::size_t index) + std::size_t size() const noexcept { - if (index > this->size()) - throw std::invalid_argument("Wrong index."); - - return *(this->buffer[index]); + return this->buffer.size(); } std::vector> buffer; diff --git a/src/vist/json/json.hpp b/src/vist/json/json.hpp index 7e1990d..1149179 100644 --- a/src/vist/json/json.hpp +++ b/src/vist/json/json.hpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -57,25 +58,10 @@ struct Json { return *(this->root.pairs[key]); } - // Return the number of 1-depth's elements. - std::size_t size() const noexcept - { - return this->root.size(); - } - - bool exist(const std::string& key) const noexcept - { - return this->root.exist(key); - } - - std::string serialize() const - { - return this->root.serialize(); - } - template void push(const std::string& key, CompositeType& child) { + auto value = std::make_shared(); auto composite = std::make_shared(); if constexpr (std::is_same_v) composite->buffer = std::move(child.buffer); @@ -84,14 +70,8 @@ struct Json { else static_assert(dependent_false::value, "Only Composite type supported."); - this->root.pairs[key] = composite; - } - - void push(const std::string& key, Array& child) - { - auto array = std::make_shared(); - array->buffer = std::move(child.buffer); - this->root.pairs[key] = array; + value->leaf = composite; + this->root.pairs[key] = std::move(value); } template @@ -102,9 +82,9 @@ struct Json { if constexpr (std::is_same_v || std::is_same_v) { - if (auto downcast = std::dynamic_pointer_cast(this->root.pairs[key]); + if (auto downcast = std::dynamic_pointer_cast(this->root.pairs[key]->leaf); downcast == nullptr) - throw std::runtime_error("Mismatched type."); + throw std::runtime_error(key + "Mismatched type."); else return *downcast; } else { @@ -112,6 +92,26 @@ struct Json { } } + std::size_t size() const noexcept + { + return this->root.size(); + } + + bool exist(const std::string& key) const noexcept + { + return this->root.exist(key); + } + + std::string serialize() const + { + return this->root.serialize(); + } + + void deserialize(const std::string& dumped) + { + this->root.deserialize(dumped); + } + Object root; }; diff --git a/src/vist/json/object.hpp b/src/vist/json/object.hpp index 8542147..df25a51 100644 --- a/src/vist/json/object.hpp +++ b/src/vist/json/object.hpp @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include @@ -25,14 +26,20 @@ namespace vist { namespace json { struct Object : public Value { - std::size_t size() const noexcept + template + void push(const std::string& key, const Type& data) { - return this->pairs.size(); + auto value = std::make_shared(); + *value = data; + this->pairs[key] = std::move(value); } - bool exist(const std::string& key) const noexcept + Value& operator[](const char* key) { - return this->pairs.find(key) != this->pairs.end(); + if (!this->exist(key)) + this->pairs[key] = std::make_shared(); + + return *(this->pairs[key]); } std::string serialize() const override @@ -55,23 +62,80 @@ struct Object : public Value { return ss.str(); } - template - void push(const std::string& key, const Type& data) + void deserialize(const std::string& dumped) override { - auto value = std::make_shared(); - *value = data; - this->pairs[key] = std::move(value); + if (dumped.empty()) + throw std::invalid_argument("Dumped value cannot empty."); + + auto stripped = strip(dumped, '{', '}'); + auto divided = split(trim(stripped), ","); + auto tokens = this->canonicalize(divided); + for (const auto& token : tokens) { + if (auto pos = token.find(":"); pos != std::string::npos) { + auto lhs = token.substr(0, pos); + auto rhs = token.substr(pos + 1); + + auto key = strip(trim(lhs), '"', '"'); + auto value = std::make_shared(); + + value->deserialize(trim(rhs)); + this->pairs[key] = value; + } else { + throw std::runtime_error("Wrong format."); + } + } } - Value& operator[](const char* key) + std::size_t size() const noexcept { - if (!this->exist(key)) - this->pairs[key] = std::make_shared(); + return this->pairs.size(); + } - return *(this->pairs[key]); + bool exist(const std::string& key) const noexcept + { + return this->pairs.find(key) != this->pairs.end(); } std::unordered_map> pairs; + + std::vector canonicalize(std::vector& tokens) + { + std::vector result; + auto rearrange = [&](std::vector::iterator& iter, char first, char last) { + if ((*iter).find(first) == std::string::npos) + return false; + + std::string origin = *iter; + iter++; + + std::size_t lcount = 1, rcount = 0; + while (iter != tokens.end()) { + if ((*iter).find(first) != std::string::npos) + lcount++; + else if ((*iter).find(last) != std::string::npos) + rcount++; + + origin += ", " + *iter; + + if (lcount == rcount) + break; + else + iter++; + } + result.emplace_back(std::move(origin)); + + return true; + }; + + for (auto iter = tokens.begin() ; iter != tokens.end(); iter++) { + if (rearrange(iter, '{', '}') || rearrange(iter, '[', ']')) + continue; + + result.emplace_back(std::move(*iter)); + } + + return result; + } }; } // namespace json diff --git a/src/vist/json/tests/json.cpp b/src/vist/json/tests/json.cpp index 6b120e0..4ecd410 100644 --- a/src/vist/json/tests/json.cpp +++ b/src/vist/json/tests/json.cpp @@ -36,6 +36,9 @@ TEST(JsonTests, int) EXPECT_EQ(static_cast(json["int"]), -1); EXPECT_EQ(json["int"].serialize(), "-1"); + + json["int"].deserialize("1"); + EXPECT_EQ(static_cast(json["int"]), 1); } TEST(JsonTests, int_type_mismatch) @@ -64,6 +67,9 @@ TEST(JsonTests, string) EXPECT_EQ(value, "changed value"); EXPECT_EQ(json["string"].serialize(), "\"changed value\""); + + json["string"].deserialize("\"deserialized value\""); + EXPECT_EQ(static_cast(json["string"]), "deserialized value"); } TEST(JsonTests, string_type_mismatch) @@ -86,11 +92,18 @@ TEST(JsonTests, array) array.push("string"); EXPECT_EQ(array.size(), 2); - EXPECT_EQ(array.serialize(), "[ 100, \"string\" ]"); EXPECT_EQ(static_cast(array.at(0)), 100); EXPECT_EQ(static_cast(array.at(1)), "string"); + auto serialized = array.serialize(); + EXPECT_EQ(serialized, "[ 100, \"string\" ]"); + + Array restore; + restore.deserialize(serialized); + EXPECT_EQ(static_cast(restore.at(0)), 100); + EXPECT_EQ(static_cast(restore.at(1)), "string"); + Json json; json.push("array", array); EXPECT_EQ(json.size(), 1); @@ -110,11 +123,18 @@ TEST(JsonTests, object) object["string"] = "initial value"; EXPECT_EQ(object.size(), 2); - EXPECT_EQ(object.serialize(), "{ \"string\": \"initial value\", \"int\": 1 }"); EXPECT_EQ(static_cast(object["int"]), 1); EXPECT_EQ(static_cast(object["string"]), "initial value"); + std::string serialized = object.serialize(); + EXPECT_EQ(serialized, "{ \"string\": \"initial value\", \"int\": 1 }"); + + Object restore; + restore.deserialize(serialized); + EXPECT_EQ(static_cast(restore["int"]), 1); + EXPECT_EQ(static_cast(restore["string"]), "initial value"); + Json json; json.push("object", object); EXPECT_EQ(json.size(), 1); @@ -132,14 +152,38 @@ TEST(JsonTests, serialize) // expected: { "string": "root value", "int": 1 } EXPECT_EQ(json.serialize(), "{ \"string\": \"root value\", \"int\": 1 }"); - Object child; - child["int"] = 2; - child["string"] = "child value"; - json.push("child", child); + Object object; + object["int"] = 2; + object["string"] = "child value"; + json.push("object", object); + + Array array; + array.push(3); + array.push("array value"); + json.push("array", array); // Json don't keep order for performance. - // expected: { "child": { "string": "child value", "int": 2 }, "int": 1, "string": "root value" } - EXPECT_EQ(json.serialize(), - "{ \"child\": { \"string\": \"child value\", " - "\"int\": 2 }, \"int\": 1, \"string\": \"root value\" }"); + // expected: { "array": [ 3, "array value" ], + // "object": { "string": "child value", "int": 2 }, + // "int": 1, + // "string": "root value" } + auto serialized = json.serialize(); + EXPECT_EQ(serialized, "{ \"array\": [ 3, \"array value\" ], " + "\"object\": { \"string\": \"child value\", \"int\": 2 }, " + "\"int\": 1, " + "\"string\": \"root value\" }"); + + Json restore; + restore.deserialize(serialized); + + EXPECT_EQ(static_cast(restore["int"]), 1); + EXPECT_EQ(static_cast(restore["string"]), "root value"); + + Object child1 = restore.get("object"); + EXPECT_EQ(static_cast(child1["int"]), 2); + EXPECT_EQ(static_cast(child1["string"]), "child value"); + + Array child2 = restore.get("array"); + EXPECT_EQ(static_cast(child2.at(0)), 3); + EXPECT_EQ(static_cast(child2.at(1)), "array value"); } diff --git a/src/vist/json/value.hpp b/src/vist/json/value.hpp index 244e005..611d98e 100644 --- a/src/vist/json/value.hpp +++ b/src/vist/json/value.hpp @@ -26,6 +26,7 @@ namespace json { struct Int; struct String; struct Object; +struct Array; template struct dependent_false : std::false_type {}; @@ -38,6 +39,27 @@ struct Value { return this->leaf->serialize(); } + template + void convert() + { + this->leaf = std::make_shared(); + } + + virtual void deserialize(const std::string& dumped) + { + if (dumped.empty()) + throw std::invalid_argument("Dumped value cannot empty."); + + switch (dumped[0]) { + case '{' : this->convert(); break; + case '[' : this->convert(); break; + case '"': this->convert(); break; + default : this->convert(); + } + + this->leaf->deserialize(dumped); + } + template Value& operator=(const Type& data) { @@ -72,15 +94,19 @@ struct Value { }; struct Int : public Value { - explicit Int(int data) : data(data) - { - } + explicit Int() {} + explicit Int(int data) : data(data) {} std::string serialize() const override { return std::to_string(data); } + void deserialize(const std::string& dumped) override + { + this->data = std::stoi(dumped); + } + operator int() override { return data; @@ -90,15 +116,25 @@ struct Int : public Value { }; struct String : public Value { - explicit String(const std::string& data) : data(data) - { - } + explicit String() {} + explicit String(const std::string& data) : data(data) {} std::string serialize() const override { return "\"" + data + "\""; } + void deserialize(const std::string& dumped) override + { + if (dumped.empty()) + throw std::invalid_argument("Dumped value cannot empty."); + + if (dumped[0] != '"' || dumped[dumped.size() - 1] != '"') + throw std::invalid_argument("Wrong format."); + + this->data = dumped.substr(1, dumped.size() -2); + } + operator std::string() override { return data; diff --git a/src/vist/string.hpp b/src/vist/string.hpp index 42424ae..c71a533 100644 --- a/src/vist/string.hpp +++ b/src/vist/string.hpp @@ -37,17 +37,29 @@ inline void rtrim(std::string& str) }).base(), str.end()); } -inline void trim(std::string& str) +inline std::string trim(const std::string& origin) { - ltrim(str); - rtrim(str); + std::string ret = origin; + ltrim(ret); + rtrim(ret); + return ret; } -inline std::vector split(const std::string origin, std::regex regex) +inline std::vector split(const std::string& origin, const std::string& expression) { + std::regex regex(expression); auto begin = std::sregex_token_iterator(origin.begin(), origin.end(), regex, -1); auto end = std::sregex_token_iterator(); return std::vector(begin, end); } +inline std::string strip(const std::string& origin, char begin, char end) +{ + auto copied = origin; + if (copied.size() < 2 || copied[0] != begin || copied[copied.size() - 1] != end) + throw std::invalid_argument("Wrong format."); + + return copied.substr(1, copied.size() -2); +} + } // namespace vist -- 2.34.1