Add serialization to JSON
authorSangwan Kwon <sangwan.kwon@samsung.com>
Thu, 14 May 2020 05:12:42 +0000 (14:12 +0900)
committer권상완/Security 2Lab(SR)/Engineer/삼성전자 <sangwan.kwon@samsung.com>
Mon, 18 May 2020 00:52:02 +0000 (09:52 +0900)
Signed-off-by: Sangwan Kwon <sangwan.kwon@samsung.com>
src/vist/common/tests/string.cpp
src/vist/json/array.hpp
src/vist/json/json.hpp
src/vist/json/object.hpp
src/vist/json/tests/json.cpp
src/vist/json/value.hpp
src/vist/string.hpp

index 371d577..6cddefa 100644 (file)
@@ -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);
+       }
+}
index 7f7083a..692e554 100644 (file)
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <vist/json/value.hpp>
+#include <vist/string.hpp>
 
 #include <string>
 #include <vector>
@@ -25,9 +26,28 @@ namespace vist {
 namespace json {
 
 struct Array : public Value {
-       std::size_t size() const noexcept
+       template <typename Type>
+       void push(const Type& data)
        {
-               return this->buffer.size();
+               auto value = std::make_shared<Value>();
+               *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 <typename Type>
-       void push(const Type& data)
+       void deserialize(const std::string& dumped) override
        {
-               auto value = std::make_shared<Value>();
-               *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>();
+                       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<std::shared_ptr<Value>> buffer;
index 7e1990d..1149179 100644 (file)
@@ -40,6 +40,7 @@
 #include <vist/json/array.hpp>
 #include <vist/json/object.hpp>
 #include <vist/json/value.hpp>
+#include <vist/string.hpp>
 
 #include <sstream>
 #include <stdexcept>
@@ -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 <typename CompositeType>
        void push(const std::string& key, CompositeType& child)
        {
+               auto value = std::make_shared<Value>();
                auto composite = std::make_shared<CompositeType>();
                if constexpr (std::is_same_v<CompositeType, Array>)
                        composite->buffer = std::move(child.buffer);
@@ -84,14 +70,8 @@ struct Json {
                else
                        static_assert(dependent_false<CompositeType>::value, "Only Composite type supported.");
 
-               this->root.pairs[key] = composite; 
-       }
-
-       void push(const std::string& key, Array& child)
-       {
-               auto array = std::make_shared<Array>();
-               array->buffer = std::move(child.buffer);
-               this->root.pairs[key] = array; 
+               value->leaf = composite;
+               this->root.pairs[key] = std::move(value);
        }
 
        template <typename CompositeType>
@@ -102,9 +82,9 @@ struct Json {
 
                if constexpr (std::is_same_v<CompositeType, Array> ||
                                          std::is_same_v<CompositeType, Object>) {
-                       if (auto downcast = std::dynamic_pointer_cast<CompositeType>(this->root.pairs[key]);
+                       if (auto downcast = std::dynamic_pointer_cast<CompositeType>(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;
 };
 
index 8542147..df25a51 100644 (file)
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <vist/json/value.hpp>
+#include <vist/string.hpp>
 
 #include <string>
 #include <unordered_map>
@@ -25,14 +26,20 @@ namespace vist {
 namespace json {
 
 struct Object : public Value {
-       std::size_t size() const noexcept
+       template <typename Type>
+       void push(const std::string& key, const Type& data)
        {
-               return this->pairs.size();
+               auto value = std::make_shared<Value>();
+               *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<Value>();
+
+               return *(this->pairs[key]);
        }
 
        std::string serialize() const override
@@ -55,23 +62,80 @@ struct Object : public Value {
                return ss.str();
        }
 
-       template <typename Type>
-       void push(const std::string& key, const Type& data)
+       void deserialize(const std::string& dumped) override
        {
-               auto value = std::make_shared<Value>();
-               *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>();
+
+                               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<Value>();
+               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<std::string, std::shared_ptr<Value>> pairs;
+
+       std::vector<std::string> canonicalize(std::vector<std::string>& tokens)
+       {
+               std::vector<std::string> result;
+               auto rearrange = [&](std::vector<std::string>::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
index 6b120e0..4ecd410 100644 (file)
@@ -36,6 +36,9 @@ TEST(JsonTests, int)
        EXPECT_EQ(static_cast<int>(json["int"]), -1);
 
        EXPECT_EQ(json["int"].serialize(), "-1");
+
+       json["int"].deserialize("1");
+       EXPECT_EQ(static_cast<int>(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<std::string>(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<int>(array.at(0)), 100);
        EXPECT_EQ(static_cast<std::string>(array.at(1)), "string");
 
+       auto serialized = array.serialize();
+       EXPECT_EQ(serialized, "[ 100, \"string\" ]");
+
+       Array restore;
+       restore.deserialize(serialized);
+       EXPECT_EQ(static_cast<int>(restore.at(0)), 100);
+       EXPECT_EQ(static_cast<std::string>(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<int>(object["int"]), 1);
        EXPECT_EQ(static_cast<std::string>(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<int>(restore["int"]), 1);
+       EXPECT_EQ(static_cast<std::string>(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<int>(restore["int"]), 1);
+       EXPECT_EQ(static_cast<std::string>(restore["string"]), "root value");
+
+       Object child1 = restore.get<Object>("object");
+       EXPECT_EQ(static_cast<int>(child1["int"]), 2);
+       EXPECT_EQ(static_cast<std::string>(child1["string"]), "child value");
+
+       Array child2 = restore.get<Array>("array");
+       EXPECT_EQ(static_cast<int>(child2.at(0)), 3);
+       EXPECT_EQ(static_cast<std::string>(child2.at(1)), "array value");
 }
index 244e005..611d98e 100644 (file)
@@ -26,6 +26,7 @@ namespace json {
 struct Int;
 struct String;
 struct Object;
+struct Array;
 
 template<class T> struct dependent_false : std::false_type {};
 
@@ -39,6 +40,27 @@ struct Value {
        }
 
        template <typename Type>
+       void convert()
+       {
+               this->leaf = std::make_shared<Type>();
+       }
+
+       virtual void deserialize(const std::string& dumped)
+       {
+               if (dumped.empty())
+                       throw std::invalid_argument("Dumped value cannot empty.");
+
+               switch (dumped[0]) {
+                       case '{' : this->convert<Object>(); break;
+                       case '[' : this->convert<Array>(); break;
+                       case '"': this->convert<String>(); break;
+                       default : this->convert<Int>();
+               }
+
+               this->leaf->deserialize(dumped);
+       }
+
+       template <typename Type>
        Value& operator=(const Type& data)
        {
                if constexpr (std::is_same_v<Type, int>)
@@ -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;
index 42424ae..c71a533 100644 (file)
@@ -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<std::string> split(const std::string origin, std::regex regex)
+inline std::vector<std::string> 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<std::string>(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