From f3aa42578db15515a230050a5667d3aeb9c50f50 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 18 Dec 2018 21:35:59 +0000 Subject: [PATCH] serialization: Implement JsonSerializer loading functionality --- include/ki/pclass/Casters.h | 34 +++++++ include/ki/pclass/Property.h | 4 + include/ki/pclass/StaticProperty.h | 39 ++++++++ include/ki/pclass/TypeSystem.h | 5 + include/ki/pclass/Value.h | 18 +++- include/ki/pclass/VectorProperty.h | 69 ++++++++++++-- include/ki/serialization/JsonSerializer.h | 5 + src/pclass/Property.cpp | 6 ++ src/pclass/Value.cpp | 31 +++++++ src/serialization/BinarySerializer.cpp | 1 - src/serialization/JsonSerializer.cpp | 108 ++++++++++++++++++++-- test/src/unit-serialization.cpp | 6 +- 12 files changed, 303 insertions(+), 23 deletions(-) diff --git a/include/ki/pclass/Casters.h b/include/ki/pclass/Casters.h index e1222ce..540c465 100644 --- a/include/ki/pclass/Casters.h +++ b/include/ki/pclass/Casters.h @@ -34,6 +34,12 @@ namespace detail template <> struct is_json_assignable : std::true_type {}; + /** + * std::u16string can be assigned to a json object. + */ + template <> + struct is_json_assignable : std::true_type {}; + /** * value_caster specialization for the generic case of casting * any json-assignable value to a json object. @@ -163,6 +169,34 @@ namespace detail } }; + /** + * Caster implementation for casting from json to any + * primitive type. + */ + template + struct value_caster + : value_caster_impl + { + DestT cast_value(const nlohmann::json &value) const override + { + return value; + } + }; + + /** + * Caster implementation for casting from json to a + * std::string. + */ + template <> + struct value_caster + : value_caster_impl + { + std::string cast_value(const nlohmann::json &value) const override + { + return value; + } + }; + /** * A utility to call ValueCaster::declare with * bi and bui as the destination type. diff --git a/include/ki/pclass/Property.h b/include/ki/pclass/Property.h index e34fb6e..b57635c 100644 --- a/include/ki/pclass/Property.h +++ b/include/ki/pclass/Property.h @@ -34,6 +34,8 @@ namespace pclass virtual bool is_dynamic() const; virtual Value get_value() const = 0; + virtual void set_value(Value value) = 0; + virtual const PropertyClass *get_object() const = 0; virtual void set_object(std::unique_ptr &object) = 0; @@ -70,12 +72,14 @@ namespace pclass virtual void set_element_count(std::size_t size) = 0; Value get_value() const final override; + void set_value(Value value) final override; const PropertyClass *get_object() const final override; void set_object(std::unique_ptr &object) final override; void write_value_to(BitStream &stream) const final override; void read_value_from(BitStream &stream) final override; virtual Value get_value(int index) const = 0; + virtual void set_value(Value value, int index) = 0; virtual const PropertyClass *get_object(int index) const = 0; virtual void set_object(std::unique_ptr &object, int index) = 0; virtual void write_value_to(BitStream &stream, int index) const = 0; diff --git a/include/ki/pclass/StaticProperty.h b/include/ki/pclass/StaticProperty.h index 4b1f78d..e572379 100644 --- a/include/ki/pclass/StaticProperty.h +++ b/include/ki/pclass/StaticProperty.h @@ -1,5 +1,6 @@ #pragma once #include "ki/pclass/Property.h" +#include "ki/pclass/PropertyClass.h" #include "ki/util/exception.h" namespace ki @@ -42,6 +43,13 @@ namespace pclass return Value::make_reference(prop.m_value); } + static void set_value(StaticProperty &prop, Value value) + { + prop.m_value = value + .dereference() + .get(); + } + static const PropertyClass *get_object(const StaticProperty &prop) { // ValueT does not derive from PropertyClass, and so, this property is not @@ -106,6 +114,13 @@ namespace pclass return Value::make_reference(*prop.m_value); } + static void set_value(StaticProperty &prop, Value value) + { + prop.m_value = value + .dereference() + .take(); + } + static const PropertyClass *get_object(const StaticProperty &prop) { // ValueT does not derive from PropertyClass, and so, this property is not @@ -173,6 +188,13 @@ namespace pclass return Value::make_reference(*prop.m_value); } + static void set_value(StaticProperty &prop, Value value) + { + prop.m_value = value + .dereference() + .take(); + } + static const PropertyClass *get_object(const StaticProperty &prop) { // ValueT does derive from PropertyClass, and we have a pointer to an instance @@ -241,6 +263,13 @@ namespace pclass return Value::make_reference(prop.m_value); } + static void set_value(StaticProperty &prop, Value value) + { + prop.m_value = value + .dereference() + .get(); + } + static const PropertyClass *get_object(const StaticProperty &prop) { // ValueT does derive from PropertyClass, and we have an instance of ValueT, @@ -343,6 +372,11 @@ namespace pclass return value_object_helper::get_value(prop); } + static void set_value(StaticProperty &prop, Value value) + { + return value_object_helper::set_value(prop, value); + } + static const PropertyClass *get_object(const StaticProperty &prop) { return value_object_helper::get_object(prop); @@ -414,6 +448,11 @@ namespace pclass return value_helper::get_value(*this); } + void set_value(Value value) override + { + value_helper::set_value(*this, value); + } + const PropertyClass *get_object() const override { return value_helper::get_object(*this); diff --git a/include/ki/pclass/TypeSystem.h b/include/ki/pclass/TypeSystem.h index c7c07cb..7a464eb 100644 --- a/include/ki/pclass/TypeSystem.h +++ b/include/ki/pclass/TypeSystem.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include "ki/pclass/HashCalculator.h" #include "ki/pclass/Casters.h" #include "ki/pclass/types/Type.h" @@ -32,6 +33,8 @@ namespace pclass PrimitiveType &define_primitive(const std::string &name) { detail::caster_declarer::declare(); + ValueCaster::declare(); + auto *type = new PrimitiveType(name, *this); define_type(std::unique_ptr( dynamic_cast(type) @@ -58,6 +61,8 @@ namespace pclass CppEnumType &define_enum(const std::string &name) { detail::caster_declarer::declare(); + ValueCaster::declare(); + auto *type = new CppEnumType(name, *this); define_type(std::unique_ptr( dynamic_cast(type) diff --git a/include/ki/pclass/Value.h b/include/ki/pclass/Value.h index 0c41d94..8d1f9c7 100644 --- a/include/ki/pclass/Value.h +++ b/include/ki/pclass/Value.h @@ -79,6 +79,11 @@ namespace pclass * @param[in] ptr The pointer to deallocate. */ virtual void deallocate(void *ptr) const = 0; + + /** + * Create a copy of this deallocator. + */ + virtual value_deallocator_base *copy() const = 0; }; /** @@ -92,18 +97,25 @@ namespace pclass // By default, now that we have the proper type, just delete it. delete static_cast(ptr); } + + value_deallocator_base *copy() const override + { + return new value_deallocator(); + } }; /** - * TODO: Documentation - */ + * TODO: Documentation + */ class ValueDeallocator { // Allow Value to call make() and the default constructor friend Value; public: + ValueDeallocator(ValueDeallocator &that); ValueDeallocator(ValueDeallocator &&that) noexcept; + ValueDeallocator &operator=(ValueDeallocator &that); ValueDeallocator &operator=(ValueDeallocator &&that) noexcept; ~ValueDeallocator(); @@ -220,7 +232,9 @@ namespace pclass class Value { public: + Value(Value &that); Value(Value &&that) noexcept; + Value &operator=(Value &that); Value &operator=(Value &&that) noexcept; ~Value(); diff --git a/include/ki/pclass/VectorProperty.h b/include/ki/pclass/VectorProperty.h index 5d69b41..58fbb1e 100644 --- a/include/ki/pclass/VectorProperty.h +++ b/include/ki/pclass/VectorProperty.h @@ -39,7 +39,15 @@ namespace pclass // Ensure index is within bounds if (index < 0 || index >= prop.size()) throw runtime_error("Index out of bounds."); - return Value::make_reference(prop.at(index)); + return Value::make_reference(prop.at(index)); + } + + static void set_value(VectorProperty &prop, + Value value, const int index) + { + prop.at(index) = value + .dereference() + .get(); } static const PropertyClass *get_object(const VectorProperty &prop, const int index) @@ -79,6 +87,8 @@ namespace pclass >::type > { + using nonpointer_type = typename std::remove_pointer::type; + static ValueT copy(VectorProperty &prop, const int index) { // Ensure index is within bounds @@ -95,7 +105,15 @@ namespace pclass // Ensure index is within bounds if (index < 0 || index >= prop.size()) throw runtime_error("Index out of bounds."); - return Value::make_reference(*prop.at(index)); + return Value::make_reference(*prop.at(index)); + } + + static void set_value(VectorProperty &prop, + Value value, const int index) + { + prop.at(index) = value + .dereference() + .take(); } static const PropertyClass *get_object(const VectorProperty &prop, const int index) @@ -135,6 +153,8 @@ namespace pclass >::type > { + using nonpointer_type = typename std::remove_pointer::type; + static ValueT copy(VectorProperty &prop, const int index) { // The copy constructor for all pointers is to copy the pointer @@ -147,7 +167,15 @@ namespace pclass // Ensure index is within bounds if (index < 0 || index >= prop.size()) throw runtime_error("Index out of bounds."); - return Value::make_reference(*prop.at(index)); + return Value::make_reference(*prop.at(index)); + } + + static void set_value(VectorProperty &prop, + Value value, const int index) + { + prop.at(index) = value + .dereference() + .take(); } static const PropertyClass *get_object(const VectorProperty &prop, const int index) @@ -214,7 +242,15 @@ namespace pclass // Ensure index is within bounds if (index < 0 || index >= prop.size()) throw runtime_error("Index out of bounds."); - return Value::make_reference(prop.at(index)); + return Value::make_reference(prop.at(index)); + } + + static void set_value(VectorProperty &prop, + Value value, const int index) + { + prop.at(index) = value + .dereference() + .get(); } static const PropertyClass *get_object(const VectorProperty &prop, const int index) @@ -326,18 +362,24 @@ namespace pclass return vector_value_object_helper::copy(prop, index); } - static const PropertyClass *get_object(const VectorProperty &prop, - const int index) - { - return vector_value_object_helper::get_object(prop, index); - } - static Value get_value(const VectorProperty &prop, const int index) { return vector_value_object_helper::get_value(prop, index); } + static void set_value(VectorProperty &prop, + Value value, const int index) + { + vector_value_object_helper::set_value(prop, value, index); + } + + static const PropertyClass *get_object(const VectorProperty &prop, + const int index) + { + return vector_value_object_helper::get_object(prop, index); + } + static void set_object(VectorProperty &prop, std::unique_ptr &object, const int index) { @@ -405,6 +447,13 @@ namespace pclass return vector_value_helper::get_value(*this, index); } + void set_value(Value value, int index) override + { + if (index < 0 || index >= this->size()) + throw runtime_error("Index out of bounds."); + return vector_value_helper::set_value(*this, value, index); + } + const PropertyClass *get_object(const int index) const override { return vector_value_helper::get_object(*this, index); diff --git a/include/ki/serialization/JsonSerializer.h b/include/ki/serialization/JsonSerializer.h index 3a9e4ec..2b627e1 100644 --- a/include/ki/serialization/JsonSerializer.h +++ b/include/ki/serialization/JsonSerializer.h @@ -30,6 +30,11 @@ namespace serialization nlohmann::json save_object(const pclass::PropertyClass *object) const; void save_property(nlohmann::json &j, const pclass::IProperty &prop) const; void save_dynamic_property(nlohmann::json &j, const pclass::IDynamicProperty &prop) const; + + virtual bool preload_object(std::unique_ptr &dest, nlohmann::json &j) const; + void load_object(std::unique_ptr &dest, nlohmann::json &j) const; + void load_property(pclass::IProperty &prop, nlohmann::json &j) const; + void load_dynamic_property(pclass::IDynamicProperty &prop, nlohmann::json &j) const; }; } } diff --git a/src/pclass/Property.cpp b/src/pclass/Property.cpp index f3877b3..09cec69 100644 --- a/src/pclass/Property.cpp +++ b/src/pclass/Property.cpp @@ -92,6 +92,12 @@ namespace pclass throw runtime_error("Called get_value() on a dynamic property. Use get_value(index) instead."); } + void IDynamicProperty::set_value(Value value) + { + // The caller must specify an index + throw runtime_error("Called set_value() on a dynamic property. Use set_value(index) instead."); + } + const PropertyClass *IDynamicProperty::get_object() const { // The caller must specify an index diff --git a/src/pclass/Value.cpp b/src/pclass/Value.cpp index a134d00..ecd1ce8 100644 --- a/src/pclass/Value.cpp +++ b/src/pclass/Value.cpp @@ -24,12 +24,23 @@ namespace pclass m_deallocator = deallocator; } + ValueDeallocator::ValueDeallocator(ValueDeallocator &that) + { + m_deallocator = that.m_deallocator->copy(); + } + ValueDeallocator::ValueDeallocator(ValueDeallocator &&that) noexcept { m_deallocator = that.m_deallocator; that.m_deallocator = nullptr; } + ValueDeallocator &ValueDeallocator::operator=(ValueDeallocator &that) + { + m_deallocator = that.m_deallocator->copy(); + return *this; + } + ValueDeallocator& ValueDeallocator::operator=(ValueDeallocator &&that) noexcept { m_deallocator = that.m_deallocator; @@ -76,6 +87,15 @@ namespace pclass m_deallocator = detail::ValueDeallocator(); } + Value::Value(Value& that) + { + m_value_ptr = that.m_value_ptr; + m_ptr_is_owned = false; + m_type_hash = that.m_type_hash; + m_caster = that.m_caster; + m_deallocator = that.m_deallocator; + } + Value::Value(Value &&that) noexcept { // Move pointer to this Value object, and take ownership if @@ -88,6 +108,17 @@ namespace pclass m_deallocator = std::move(that.m_deallocator); } + Value &Value::operator=(Value &that) + { + m_value_ptr = that.m_value_ptr; + m_ptr_is_owned = false; + m_type_hash = that.m_type_hash; + m_caster = that.m_caster; + m_deallocator = that.m_deallocator; + + return *this; + } + Value &Value::operator=(Value &&that) noexcept { // If the current pointer is owned, deallocate it diff --git a/src/serialization/BinarySerializer.cpp b/src/serialization/BinarySerializer.cpp index d03c6da..1570191 100644 --- a/src/serialization/BinarySerializer.cpp +++ b/src/serialization/BinarySerializer.cpp @@ -435,6 +435,5 @@ namespace serialization prop.read_value_from(stream, i); } } - } } diff --git a/src/serialization/JsonSerializer.cpp b/src/serialization/JsonSerializer.cpp index ba2780e..51fe460 100644 --- a/src/serialization/JsonSerializer.cpp +++ b/src/serialization/JsonSerializer.cpp @@ -1,4 +1,5 @@ #include "ki/serialization/JsonSerializer.h" +#include using namespace nlohmann; @@ -54,7 +55,6 @@ namespace serialization { if (prop.is_dynamic()) { - // Cast the property to a IDynamicProperty const auto &dynamic_property = dynamic_cast(prop); return save_dynamic_property(j, dynamic_property); @@ -94,12 +94,108 @@ namespace serialization j[prop.get_name()] = property_value; } - void JsonSerializer::load(std::unique_ptr& dest, - const std::string& json_string) const + void JsonSerializer::load(std::unique_ptr &dest, + const std::string &json_string) const { - // TODO: JSON Deserialization - auto j = json::parse(json_string); - dest = nullptr; + try + { + auto j = json::parse(json_string); + load_object(dest, j); + } + catch (json::exception &e) + { + std::ostringstream oss; + oss << "Failed to deserialize JSON - " << e.what(); + throw runtime_error(oss.str()); + } + } + + bool JsonSerializer::preload_object( + std::unique_ptr& dest, json& j) const + { + // If meta information is not present, assume that the type hash is null. + if (!j.count("_pclass_meta")) + dest = nullptr; + else + { + // Use the type hash to instantiate an object + const auto type_hash = j["_pclass_meta"].value("type_hash", NULL); + if (type_hash != 0) + { + const auto &type = m_type_system->get_type(type_hash); + dest = type.instantiate(); + } + else + dest = nullptr; + } + return dest != nullptr; + } + + void JsonSerializer::load_object( + std::unique_ptr &dest, json &j) const + { + if (!preload_object(dest, j)) + return; + + auto &property_list = dest->get_properties(); + for (auto it = property_list.begin(); + it != property_list.end(); ++it) + { + auto &prop = *it; + load_property(prop, j); + } + } + + void JsonSerializer::load_property( + pclass::IProperty &prop, json &j) const + { + if (!j.count(prop.get_name())) + { + std::ostringstream oss; + oss << "JSON object missing property: '" << prop.get_name() << "' " + << "(type='" << prop.get_instance().get_type().get_name() << "')."; + throw runtime_error(oss.str()); + } + + auto &property_j = j[prop.get_name()]; + if (prop.is_dynamic()) + { + auto &dynamic_property = + dynamic_cast(prop); + return load_dynamic_property(dynamic_property, j); + } + + if (prop.get_type().get_kind() == pclass::Type::kind::CLASS) + { + std::unique_ptr other_object = nullptr; + load_object(other_object, property_j); + prop.set_object(other_object); + } + else + prop.set_value( + pclass::Value::make_reference(property_j) + ); + } + + void JsonSerializer::load_dynamic_property( + pclass::IDynamicProperty &prop, json &j) const + { + auto &property_j = j[prop.get_name()]; + prop.set_element_count(property_j.size()); + + for (auto i = 0; i < prop.get_element_count(); ++i) + { + if (prop.get_type().get_kind() == pclass::Type::kind::CLASS) + { + std::unique_ptr other_object = nullptr; + load_object(other_object, property_j.at(i)); + prop.set_object(other_object, i); + } + else + prop.set_value( + pclass::Value::make_reference(property_j.at(i)), i + ); + } } } } diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index f3bff89..5277472 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -108,7 +108,7 @@ namespace detail struct value_caster : value_caster_impl { - nlohmann::json cast_value(const Vector3D &value) const + nlohmann::json cast_value(const Vector3D &value) const override { return { { "x", value.m_x }, @@ -125,7 +125,7 @@ namespace detail struct value_caster : value_caster_impl { - Vector3D cast_value(const nlohmann::json &value) const + Vector3D cast_value(const nlohmann::json &value) const override { return Vector3D( value["x"].get(), @@ -562,13 +562,11 @@ void test_serializer( serializer.load(object, sample); // Set test_object so that it is validated by the caller - /* REQUIRE(object != nullptr); test_object = std::unique_ptr( dynamic_cast(object.release()) ); REQUIRE(test_object != nullptr); - */ } }