diff options
author | Jeroen Bakker <jbakker> | 2021-10-26 14:05:59 +0300 |
---|---|---|
committer | Jeroen Bakker <jeroen@blender.org> | 2021-10-26 14:09:10 +0300 |
commit | ddf97d6270d0a0da36257bb676d9d05513064de5 (patch) | |
tree | a7b50b034db2ee79f740e7c228757fb7918a8228 /source/blender/blenlib/intern | |
parent | d20fa6c4d48580bf5fc140cf83fd7d8829bd8e09 (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.cc | 217 |
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 + |