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");
}
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");
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);
+ }
+}
#pragma once
#include <vist/json/value.hpp>
+#include <vist/string.hpp>
#include <string>
#include <vector>
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
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;
#include <vist/json/array.hpp>
#include <vist/json/object.hpp>
#include <vist/json/value.hpp>
+#include <vist/string.hpp>
#include <sstream>
#include <stdexcept>
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);
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>
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 {
}
}
+ 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;
};
#pragma once
#include <vist/json/value.hpp>
+#include <vist/string.hpp>
#include <string>
#include <unordered_map>
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
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
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)
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)
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);
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);
// 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");
}
struct Int;
struct String;
struct Object;
+struct Array;
template<class T> struct dependent_false : std::false_type {};
}
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>)
};
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;
};
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;
}).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