Usable JSON header-only library.
- Applied design pattern: Composite pattern
- Component structure: Value
- Leaf structure: Int, String (To be added: Bool, Null)
- Composite structure: Object (To be added: Array)
Usage:
// Set json object
Json json;
json["name"] = "sangwan";
json["age"] = 99;
// Get json value
std::string name = json["name"];
int age = json["age"];
// Serialize json value
std::string serialized = json.serialize();
Signed-off-by: Sangwan Kwon <sangwan.kwon@samsung.com>
ADD_SUBDIRECTORY(common)
ADD_SUBDIRECTORY(database)
ADD_SUBDIRECTORY(event)
+ADD_SUBDIRECTORY(json)
ADD_SUBDIRECTORY(klass)
ADD_SUBDIRECTORY(logger)
ADD_SUBDIRECTORY(query-builder)
--- /dev/null
+# Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License
+
+FILE(GLOB JSON_TESTS "tests/*.cpp")
+ADD_VIST_TEST(${JSON_TESTS})
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the language governing permissions and
+ * limitations under the License
+ */
+/*
+ * Usable JSON header-only library.
+ * - Applied design pattern: Composite pattern
+ * - Component structure: Value
+ * - Leaf structure: Int, String (To be added: Bool, Null)
+ * - Composite structure: Object (To be added: Array)
+ */
+/*
+ * Usage:
+ * // Set json object
+ * Json json;
+ * json["name"] = "sangwan";
+ * json["age"] = 99;
+ *
+ * // Get json value
+ * std::string name = json["name"];
+ * int age = json["age"];
+ *
+ * // Serialize json value
+ * std::string serialized = json.serialize();
+ */
+
+#pragma once
+
+#include <vist/json/value.hpp>
+#include <vist/json/object.hpp>
+
+#include <sstream>
+#include <stdexcept>
+#include <string>
+
+namespace vist {
+namespace json {
+
+struct Json {
+ Value& operator[](const std::string& key)
+ {
+ if (!this->root.exist(key))
+ this->root.pairs[key] = std::make_shared<Value>();
+
+ 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();
+ }
+
+ void push(const std::string& key, Json& child)
+ {
+ auto object = std::make_shared<Object>();
+ object->pairs = std::move(child.root.pairs);
+ this->root.pairs[key] = object;
+ }
+
+ Json pop(const std::string& key)
+ {
+ if (!this->root.exist(key))
+ throw std::runtime_error("Not exist key.");
+
+ if (auto downcast = std::dynamic_pointer_cast<Object>(this->root.pairs[key]);
+ downcast == nullptr)
+ throw std::runtime_error("Mismatched type.");
+ else {
+ Json json;
+ json.root.pairs = std::move(downcast->pairs);
+ this->root.pairs.erase(key);
+ return json;
+ }
+ }
+
+ Object root;
+};
+
+} // namespace json
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the language governing permissions and
+ * limitations under the License
+ */
+
+#pragma once
+
+#include <vist/json/value.hpp>
+
+#include <string>
+#include <unordered_map>
+
+namespace vist {
+namespace json {
+
+struct Object : public Value {
+ std::size_t size() const noexcept
+ {
+ return this->pairs.size();
+ }
+
+ bool exist(const std::string& key) const noexcept
+ {
+ return this->pairs.find(key) != this->pairs.end();
+ }
+
+ std::string serialize() const override
+ {
+ std::stringstream ss;
+ ss << "{ ";
+
+ std::size_t i = 0;
+ for (const auto& [key, value] : pairs) {
+ ss << "\"" << key << "\": ";
+ ss << value->serialize();
+
+ if (i++ < pairs.size() - 1)
+ ss << ",";
+
+ ss << " ";
+ }
+ ss << "}";
+
+ return ss.str();
+ }
+
+ Value& operator[](const char* key)
+ {
+ if (!this->exist(key))
+ this->pairs[key] = std::make_shared<Value>();
+
+ return *(this->pairs[key]);
+ }
+
+ std::unordered_map<std::string, std::shared_ptr<Value>> pairs;
+};
+
+} // namespace json
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#include <gtest/gtest.h>
+
+#include <vist/json/json.hpp>
+#include <vist/logger.hpp>
+
+using namespace vist::json;
+
+TEST(JsonTests, int)
+{
+ Json json;
+ json["int"] = 1;
+
+ int value = json["int"];
+ EXPECT_EQ(value, 1);
+
+ json["int"] = -1;
+ value = json["int"];
+ EXPECT_EQ(value, -1);
+
+ EXPECT_EQ(static_cast<int>(json["int"]), -1);
+
+ EXPECT_EQ(json["int"].serialize(), "-1");
+}
+
+TEST(JsonTests, int_type_mismatch)
+{
+ Json json;
+ json["int"] = 1;
+
+ try {
+ static_cast<std::string>(json["int"]);
+ EXPECT_TRUE(false);
+ } catch(...) {
+ EXPECT_TRUE(true);
+ }
+}
+
+TEST(JsonTests, string)
+{
+ Json json;
+ json["string"] = "initial value";
+
+ std::string value = json["string"];
+ EXPECT_EQ(value, "initial value");
+
+ json["string"] = "changed value";
+ value = static_cast<std::string>(json["string"]);
+ EXPECT_EQ(value, "changed value");
+
+ EXPECT_EQ(json["string"].serialize(), "\"changed value\"");
+}
+
+TEST(JsonTests, string_type_mismatch)
+{
+ Json json;
+ json["string"] = "initial value";
+
+ try {
+ static_cast<int>(json["string"]);
+ EXPECT_TRUE(false);
+ } catch(...) {
+ EXPECT_TRUE(true);
+ }
+}
+
+TEST(JsonTests, object)
+{
+ Json root, child;
+ child["int"] = 1;
+ child["string"] = "initial value";
+
+ root.push("child", child);
+ EXPECT_EQ(root.size(), 1);
+
+ auto result = root.pop("child");
+ EXPECT_EQ(root.size(), 0);
+ EXPECT_EQ(result.size(), 2);
+
+ EXPECT_EQ(static_cast<int>(result["int"]), 1);
+ EXPECT_EQ(static_cast<std::string>(result["string"]), "initial value");
+}
+
+TEST(JsonTests, serialize)
+{
+ Json json;
+ json["int"] = 1;
+ json["string"] = "root value";
+ // expected: { "string": "root value", "int": 1 }
+ EXPECT_EQ(json.serialize(), "{ \"string\": \"root value\", \"int\": 1 }");
+
+ Json child;
+ child["int"] = 2;
+ child["string"] = "child value";
+ json.push("child", child);
+
+ // 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\" }");
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the language governing permissions and
+ * limitations under the License
+ */
+
+#pragma once
+
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+
+namespace vist {
+namespace json {
+
+struct Int;
+struct String;
+struct Object;
+
+template<class T> struct dependent_false : std::false_type {};
+
+struct Value {
+ virtual std::string serialize() const
+ {
+ if (leaf == nullptr)
+ throw std::runtime_error("Leaf is not set yet.");
+
+ return this->leaf->serialize();
+ }
+
+ template <typename Type>
+ Value& operator=(const Type& data)
+ {
+ if constexpr (std::is_same_v<Type, int>)
+ this->leaf = std::make_shared<Int>(data);
+ else if constexpr (std::is_same_v<typename std::decay<Type>::type, std::string> ||
+ std::is_same_v<typename std::decay<Type>::type, char*>)
+ this->leaf = std::make_shared<String>(data);
+ else
+ static_assert(dependent_false<Type>::value, "Not supported type.");
+
+ return *this;
+ }
+
+ virtual operator int()
+ {
+ if (auto downcast = std::dynamic_pointer_cast<Int>(this->leaf); downcast == nullptr)
+ throw std::runtime_error("Mismatched type.");
+
+ return (*this->leaf).operator int();
+ }
+
+ virtual operator std::string()
+ {
+ if (auto downcast = std::dynamic_pointer_cast<String>(this->leaf); downcast == nullptr)
+ throw std::runtime_error("Mismatched type.");
+
+ return (*this->leaf).operator std::string();
+ }
+
+ std::shared_ptr<Value> leaf;
+};
+
+struct Int : public Value {
+ explicit Int(int data) : data(data)
+ {
+ }
+
+ std::string serialize() const override
+ {
+ return std::to_string(data);
+ }
+
+ operator int() override
+ {
+ return data;
+ }
+
+ int data = 0;
+};
+
+struct String : public Value {
+ explicit String(const std::string& data) : data(data)
+ {
+ }
+
+ std::string serialize() const override
+ {
+ return "\"" + data + "\"";
+ }
+
+ operator std::string() override
+ {
+ return data;
+ }
+
+ std::string data;
+};
+
+} // namespace json
+} // namespace vist