Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeroen Bakker <jbakker>2021-10-26 14:05:59 +0300
committerJeroen Bakker <jeroen@blender.org>2021-10-26 14:09:10 +0300
commitddf97d6270d0a0da36257bb676d9d05513064de5 (patch)
treea7b50b034db2ee79f740e7c228757fb7918a8228 /source/blender/blenlib/intern
parentd20fa6c4d48580bf5fc140cf83fd7d8829bd8e09 (diff)
BlenLib: Add JSON Serialization/Deserialization Abstraction Layer.
Adds an abstraction layer to switch between serialization formats. Currently only supports JSON. The abstraction layer supports `String`, `Int`, `Array`, `Null`, `Boolean`, `Float` and `Object`. This feature is only CPP complaint. To write from a stream, the structure can be built by creating a value (any subclass of `blender::io::serialize::Value` can do, and pass it to the `serialize` method of a `blender::io::serialize::Formatter`. The formatter is abstract and there is one implementation for JSON (`JsonFormatter`). To read from a stream use the `deserialize` method of the formatter. {D12693} uses this abstraction layer to read/write asset indexes. Reviewed By: Severin, sybren Maniphest Tasks: T91430 Differential Revision: https://developer.blender.org/D12544
Diffstat (limited to 'source/blender/blenlib/intern')
-rw-r--r--source/blender/blenlib/intern/serialize.cc217
1 files changed, 217 insertions, 0 deletions
diff --git a/source/blender/blenlib/intern/serialize.cc b/source/blender/blenlib/intern/serialize.cc
new file mode 100644
index 00000000000..1e023a9f782
--- /dev/null
+++ b/source/blender/blenlib/intern/serialize.cc
@@ -0,0 +1,217 @@
+#include "BLI_serialize.hh"
+
+#include "json.hpp"
+
+namespace blender::io::serialize {
+
+const StringValue *Value::as_string_value() const
+{
+ if (type_ != eValueType::String) {
+ return nullptr;
+ }
+ return static_cast<const StringValue *>(this);
+}
+
+const IntValue *Value::as_int_value() const
+{
+ if (type_ != eValueType::Int) {
+ return nullptr;
+ }
+ return static_cast<const IntValue *>(this);
+}
+
+const DoubleValue *Value::as_double_value() const
+{
+ if (type_ != eValueType::Double) {
+ return nullptr;
+ }
+ return static_cast<const DoubleValue *>(this);
+}
+
+const BooleanValue *Value::as_boolean_value() const
+{
+ if (type_ != eValueType::Boolean) {
+ return nullptr;
+ }
+ return static_cast<const BooleanValue *>(this);
+}
+
+const ArrayValue *Value::as_array_value() const
+{
+ if (type_ != eValueType::Array) {
+ return nullptr;
+ }
+ return static_cast<const ArrayValue *>(this);
+}
+
+const ObjectValue *Value::as_object_value() const
+{
+ if (type_ != eValueType::Object) {
+ return nullptr;
+ }
+ return static_cast<const ObjectValue *>(this);
+}
+
+static void convert_to_json(nlohmann::ordered_json &j, const Value &value);
+static void convert_to_json(nlohmann::ordered_json &j, const ArrayValue &value)
+{
+ const ArrayValue::Items &items = value.elements();
+ /* Create a json array to store the elements. If this isn't done and items is empty it would
+ * return use a null value, in stead of an empty array. */
+ j = "[]"_json;
+ for (const ArrayValue::Item &item_value : items) {
+ nlohmann::ordered_json json_item;
+ convert_to_json(json_item, *item_value);
+ j.push_back(json_item);
+ }
+}
+
+static void convert_to_json(nlohmann::ordered_json &j, const ObjectValue &value)
+{
+ const ObjectValue::Items &attributes = value.elements();
+ /* Create a json object to store the attributes. If this isn't done and attributes is empty it
+ * would return use a null value, in stead of an empty object. */
+ j = "{}"_json;
+ for (const ObjectValue::Item &attribute : attributes) {
+ nlohmann::ordered_json json_item;
+ convert_to_json(json_item, *attribute.second);
+ j[attribute.first] = json_item;
+ }
+}
+
+static void convert_to_json(nlohmann::ordered_json &j, const Value &value)
+{
+ switch (value.type()) {
+ case eValueType::String: {
+ j = value.as_string_value()->value();
+ break;
+ }
+
+ case eValueType::Int: {
+ j = value.as_int_value()->value();
+ break;
+ }
+
+ case eValueType::Array: {
+ const ArrayValue &array = *value.as_array_value();
+ convert_to_json(j, array);
+ break;
+ }
+
+ case eValueType::Object: {
+ const ObjectValue &object = *value.as_object_value();
+ convert_to_json(j, object);
+ break;
+ }
+
+ case eValueType::Null: {
+ j = nullptr;
+ break;
+ }
+
+ case eValueType::Boolean: {
+ j = value.as_boolean_value()->value();
+ break;
+ }
+
+ case eValueType::Double: {
+ j = value.as_double_value()->value();
+ }
+ }
+}
+
+static std::unique_ptr<Value> convert_from_json(const nlohmann::ordered_json &j);
+static std::unique_ptr<ArrayValue> convert_from_json_to_array(const nlohmann::ordered_json &j)
+{
+ std::unique_ptr<ArrayValue> array = std::make_unique<ArrayValue>();
+ ArrayValue::Items &elements = array->elements();
+ for (auto element : j.items()) {
+ nlohmann::ordered_json element_json = element.value();
+ std::unique_ptr<Value> value = convert_from_json(element_json);
+ elements.append_as(value.release());
+ }
+ return array;
+}
+
+static std::unique_ptr<ObjectValue> convert_from_json_to_object(const nlohmann::ordered_json &j)
+{
+ std::unique_ptr<ObjectValue> object = std::make_unique<ObjectValue>();
+ ObjectValue::Items &elements = object->elements();
+ for (auto element : j.items()) {
+ std::string key = element.key();
+ nlohmann::ordered_json element_json = element.value();
+ std::unique_ptr<Value> value = convert_from_json(element_json);
+ elements.append_as(std::pair(key, value.release()));
+ }
+ return object;
+}
+
+static std::unique_ptr<Value> convert_from_json(const nlohmann::ordered_json &j)
+{
+ switch (j.type()) {
+ case nlohmann::json::value_t::array: {
+ return convert_from_json_to_array(j);
+ }
+
+ case nlohmann::json::value_t::object: {
+ return convert_from_json_to_object(j);
+ }
+
+ case nlohmann::json::value_t::string: {
+ std::string value = j;
+ return std::make_unique<StringValue>(value);
+ }
+
+ case nlohmann::json::value_t::null: {
+ return std::make_unique<NullValue>();
+ }
+
+ case nlohmann::json::value_t::boolean: {
+ return std::make_unique<BooleanValue>(j);
+ }
+ case nlohmann::json::value_t::number_integer:
+ case nlohmann::json::value_t::number_unsigned: {
+ return std::make_unique<IntValue>(j);
+ }
+
+ case nlohmann::json::value_t::number_float: {
+ return std::make_unique<DoubleValue>(j);
+ }
+
+ case nlohmann::json::value_t::binary:
+ case nlohmann::json::value_t::discarded:
+ /*
+ * Binary data isn't supported.
+ * Discarded is an internal type of nlohmann.
+ *
+ * Assert in case we need to parse them.
+ */
+ BLI_assert_unreachable();
+ return std::make_unique<NullValue>();
+ }
+
+ BLI_assert_unreachable();
+ return std::make_unique<NullValue>();
+}
+
+void JsonFormatter::serialize(std::ostream &os, const Value &value)
+{
+ nlohmann::ordered_json j;
+ convert_to_json(j, value);
+ if (indentation_len) {
+ os << j.dump(indentation_len);
+ }
+ else {
+ os << j.dump();
+ }
+}
+
+std::unique_ptr<Value> JsonFormatter::deserialize(std::istream &is)
+{
+ nlohmann::ordered_json j;
+ is >> j;
+ return convert_from_json(j);
+}
+
+} // namespace blender::io::serialize
+