serialization: Implement JsonSerializer loading functionality

This commit is contained in:
Joshua Scott 2018-12-18 21:35:59 +00:00
parent f9ab6560d9
commit f3aa42578d
12 changed files with 303 additions and 23 deletions

View File

@ -34,6 +34,12 @@ namespace detail
template <>
struct is_json_assignable<std::string> : std::true_type {};
/**
* std::u16string can be assigned to a json object.
*/
template <>
struct is_json_assignable<std::u16string> : 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 <typename DestT>
struct value_caster<nlohmann::json, DestT>
: value_caster_impl<nlohmann::json, DestT>
{
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<nlohmann::json, std::string>
: value_caster_impl<nlohmann::json, std::string>
{
std::string cast_value(const nlohmann::json &value) const override
{
return value;
}
};
/**
* A utility to call ValueCaster::declare<SrcT, DestT> with
* bi<N> and bui<N> as the destination type.

View File

@ -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<PropertyClass> &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<PropertyClass> &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<PropertyClass> &object, int index) = 0;
virtual void write_value_to(BitStream &stream, int index) const = 0;

View File

@ -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<ValueT>(prop.m_value);
}
static void set_value(StaticProperty<ValueT> &prop, Value value)
{
prop.m_value = value
.dereference<ValueT>()
.get<ValueT>();
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
// ValueT does not derive from PropertyClass, and so, this property is not
@ -106,6 +114,13 @@ namespace pclass
return Value::make_reference<nonpointer_type>(*prop.m_value);
}
static void set_value(StaticProperty<ValueT> &prop, Value value)
{
prop.m_value = value
.dereference<nonpointer_type>()
.take<nonpointer_type>();
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
// ValueT does not derive from PropertyClass, and so, this property is not
@ -173,6 +188,13 @@ namespace pclass
return Value::make_reference<nonpointer_type>(*prop.m_value);
}
static void set_value(StaticProperty<ValueT> &prop, Value value)
{
prop.m_value = value
.dereference<nonpointer_type>()
.take<nonpointer_type>();
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
// ValueT does derive from PropertyClass, and we have a pointer to an instance
@ -241,6 +263,13 @@ namespace pclass
return Value::make_reference<ValueT>(prop.m_value);
}
static void set_value(StaticProperty<ValueT> &prop, Value value)
{
prop.m_value = value
.dereference<ValueT>()
.get<ValueT>();
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
// ValueT does derive from PropertyClass, and we have an instance of ValueT,
@ -343,6 +372,11 @@ namespace pclass
return value_object_helper<ValueT>::get_value(prop);
}
static void set_value(StaticProperty<ValueT> &prop, Value value)
{
return value_object_helper<ValueT>::set_value(prop, value);
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
return value_object_helper<ValueT>::get_object(prop);
@ -414,6 +448,11 @@ namespace pclass
return value_helper<ValueT>::get_value(*this);
}
void set_value(Value value) override
{
value_helper<ValueT>::set_value(*this, value);
}
const PropertyClass *get_object() const override
{
return value_helper<ValueT>::get_object(*this);

View File

@ -1,6 +1,7 @@
#pragma once
#include <vector>
#include <unordered_map>
#include <json.hpp>
#include "ki/pclass/HashCalculator.h"
#include "ki/pclass/Casters.h"
#include "ki/pclass/types/Type.h"
@ -32,6 +33,8 @@ namespace pclass
PrimitiveType<ValueT> &define_primitive(const std::string &name)
{
detail::caster_declarer<ValueT>::declare();
ValueCaster::declare<nlohmann::json, ValueT>();
auto *type = new PrimitiveType<ValueT>(name, *this);
define_type(std::unique_ptr<Type>(
dynamic_cast<Type *>(type)
@ -58,6 +61,8 @@ namespace pclass
CppEnumType<EnumT> &define_enum(const std::string &name)
{
detail::caster_declarer<EnumT>::declare();
ValueCaster::declare<nlohmann::json, EnumT>();
auto *type = new CppEnumType<EnumT>(name, *this);
define_type(std::unique_ptr<Type>(
dynamic_cast<Type *>(type)

View File

@ -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<T *>(ptr);
}
value_deallocator_base *copy() const override
{
return new value_deallocator<T>();
}
};
/**
* TODO: Documentation
*/
* TODO: Documentation
*/
class ValueDeallocator
{
// Allow Value to call make<T>() 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();

View File

@ -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<ValueT>(prop.at(index));
}
static void set_value(VectorProperty<ValueT> &prop,
Value value, const int index)
{
prop.at(index) = value
.dereference<ValueT>()
.get<ValueT>();
}
static const PropertyClass *get_object(const VectorProperty<ValueT> &prop, const int index)
@ -79,6 +87,8 @@ namespace pclass
>::type
>
{
using nonpointer_type = typename std::remove_pointer<ValueT>::type;
static ValueT copy(VectorProperty<ValueT> &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<nonpointer_type>(*prop.at(index));
}
static void set_value(VectorProperty<ValueT> &prop,
Value value, const int index)
{
prop.at(index) = value
.dereference<nonpointer_type>()
.take<nonpointer_type>();
}
static const PropertyClass *get_object(const VectorProperty<ValueT> &prop, const int index)
@ -135,6 +153,8 @@ namespace pclass
>::type
>
{
using nonpointer_type = typename std::remove_pointer<ValueT>::type;
static ValueT copy(VectorProperty<ValueT> &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<nonpointer_type>(*prop.at(index));
}
static void set_value(VectorProperty<ValueT> &prop,
Value value, const int index)
{
prop.at(index) = value
.dereference<nonpointer_type>()
.take<nonpointer_type>();
}
static const PropertyClass *get_object(const VectorProperty<ValueT> &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<ValueT>(prop.at(index));
}
static void set_value(VectorProperty<ValueT> &prop,
Value value, const int index)
{
prop.at(index) = value
.dereference<ValueT>()
.get<ValueT>();
}
static const PropertyClass *get_object(const VectorProperty<ValueT> &prop, const int index)
@ -326,18 +362,24 @@ namespace pclass
return vector_value_object_helper<ValueT>::copy(prop, index);
}
static const PropertyClass *get_object(const VectorProperty<ValueT> &prop,
const int index)
{
return vector_value_object_helper<ValueT>::get_object(prop, index);
}
static Value get_value(const VectorProperty<ValueT> &prop,
const int index)
{
return vector_value_object_helper<ValueT>::get_value(prop, index);
}
static void set_value(VectorProperty<ValueT> &prop,
Value value, const int index)
{
vector_value_object_helper<ValueT>::set_value(prop, value, index);
}
static const PropertyClass *get_object(const VectorProperty<ValueT> &prop,
const int index)
{
return vector_value_object_helper<ValueT>::get_object(prop, index);
}
static void set_object(VectorProperty<ValueT> &prop,
std::unique_ptr<PropertyClass> &object, const int index)
{
@ -405,6 +447,13 @@ namespace pclass
return vector_value_helper<ValueT>::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<ValueT>::set_value(*this, value, index);
}
const PropertyClass *get_object(const int index) const override
{
return vector_value_helper<ValueT>::get_object(*this, index);

View File

@ -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<pclass::PropertyClass> &dest, nlohmann::json &j) const;
void load_object(std::unique_ptr<pclass::PropertyClass> &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;
};
}
}

View File

@ -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

View File

@ -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

View File

@ -435,6 +435,5 @@ namespace serialization
prop.read_value_from(stream, i);
}
}
}
}

View File

@ -1,4 +1,5 @@
#include "ki/serialization/JsonSerializer.h"
#include <sstream>
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<const pclass::IDynamicProperty &>(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<pclass::PropertyClass>& dest,
const std::string& json_string) const
void JsonSerializer::load(std::unique_ptr<pclass::PropertyClass> &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<pclass::PropertyClass>& 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<pclass::PropertyClass> &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<pclass::IDynamicProperty &>(prop);
return load_dynamic_property(dynamic_property, j);
}
if (prop.get_type().get_kind() == pclass::Type::kind::CLASS)
{
std::unique_ptr<pclass::PropertyClass> other_object = nullptr;
load_object(other_object, property_j);
prop.set_object(other_object);
}
else
prop.set_value(
pclass::Value::make_reference<json>(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<pclass::PropertyClass> 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<json>(property_j.at(i)), i
);
}
}
}
}

View File

@ -108,7 +108,7 @@ namespace detail
struct value_caster<Vector3D, nlohmann::json>
: value_caster_impl<Vector3D, nlohmann::json>
{
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<nlohmann::json, Vector3D>
: value_caster_impl<nlohmann::json, Vector3D>
{
Vector3D cast_value(const nlohmann::json &value) const
Vector3D cast_value(const nlohmann::json &value) const override
{
return Vector3D(
value["x"].get<float>(),
@ -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<TestObject>(
dynamic_cast<TestObject *>(object.release())
);
REQUIRE(test_object != nullptr);
*/
}
}