mirror of https://github.com/SeanOMik/libki.git
serialization: Implement saving via JsonSerializer
This commit is contained in:
parent
def6549bcb
commit
1a3dfbea48
|
@ -64,19 +64,7 @@ namespace serialization
|
||||||
* @param object
|
* @param object
|
||||||
* @param stream
|
* @param stream
|
||||||
*/
|
*/
|
||||||
virtual void presave_object(const pclass::PropertyClass *object, BitStream &stream) const;
|
virtual bool presave_object(const pclass::PropertyClass *object, BitStream &stream) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param object
|
|
||||||
* @param stream
|
|
||||||
*/
|
|
||||||
void save_object(const pclass::PropertyClass *object, BitStream &stream) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param prop
|
|
||||||
* @param stream
|
|
||||||
*/
|
|
||||||
void save_property(const pclass::IProperty &prop, BitStream &stream) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read an object header, and instantiate the necessary PropertyClass.
|
* Read an object header, and instantiate the necessary PropertyClass.
|
||||||
|
@ -86,25 +74,22 @@ namespace serialization
|
||||||
virtual void preload_object(
|
virtual void preload_object(
|
||||||
std::unique_ptr<pclass::PropertyClass> &dest, BitStream &stream) const;
|
std::unique_ptr<pclass::PropertyClass> &dest, BitStream &stream) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param dest
|
|
||||||
* @param stream
|
|
||||||
*/
|
|
||||||
void load_object(
|
|
||||||
std::unique_ptr<pclass::PropertyClass> &dest, BitStream &stream) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param prop
|
|
||||||
* @param stream
|
|
||||||
*/
|
|
||||||
void load_property(pclass::IProperty &prop, BitStream &stream) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const pclass::TypeSystem *m_type_system;
|
const pclass::TypeSystem *m_type_system;
|
||||||
bool m_is_file;
|
bool m_is_file;
|
||||||
flags m_flags;
|
flags m_flags;
|
||||||
|
|
||||||
const pclass::PropertyClass *m_root_object;
|
const pclass::PropertyClass *m_root_object;
|
||||||
|
|
||||||
|
void save_object(const pclass::PropertyClass *object, BitStream &stream) const;
|
||||||
|
void save_property(const pclass::IProperty &prop, BitStream &stream) const;
|
||||||
|
void save_dynamic_property(
|
||||||
|
const pclass::IDynamicProperty &prop, BitStream &stream) const;
|
||||||
|
|
||||||
|
void load_object(
|
||||||
|
std::unique_ptr<pclass::PropertyClass> &dest, BitStream &stream) const;
|
||||||
|
void load_property(pclass::IProperty &prop, BitStream &stream) const;
|
||||||
|
void load_dynamic_property(pclass::IDynamicProperty &prop, BitStream &stream) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
#include <json.hpp>
|
||||||
|
#include "ki/pclass/Property.h"
|
||||||
|
#include "ki/pclass/TypeSystem.h"
|
||||||
|
|
||||||
|
namespace ki
|
||||||
|
{
|
||||||
|
namespace serialization
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* TODO: Documentation
|
||||||
|
*/
|
||||||
|
class JsonSerializer
|
||||||
|
{
|
||||||
|
static const int FILE_INDENT_VALUE = 2;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit JsonSerializer(pclass::TypeSystem &type_system, bool is_file);
|
||||||
|
virtual ~JsonSerializer() {}
|
||||||
|
|
||||||
|
std::string save(const pclass::PropertyClass *object) const;
|
||||||
|
void load(std::unique_ptr<pclass::PropertyClass> &dest, const std::string &json_string) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
pclass::TypeSystem *m_type_system;
|
||||||
|
bool m_is_file;
|
||||||
|
|
||||||
|
virtual bool presave_object(nlohmann::json &j, const pclass::PropertyClass *object) const;
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -114,22 +114,20 @@ namespace serialization
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinarySerializer::presave_object(const pclass::PropertyClass *object, BitStream &stream) const
|
bool BinarySerializer::presave_object(const pclass::PropertyClass *object, BitStream &stream) const
|
||||||
{
|
{
|
||||||
// If we have an object, write the type hash, otherwise, write NULL (0).
|
// If we have an object, write the type hash, otherwise, write NULL (0).
|
||||||
if (object)
|
if (object)
|
||||||
stream.write<uint32_t>(object->get_type().get_hash());
|
stream.write<uint32_t>(object->get_type().get_hash());
|
||||||
else
|
else
|
||||||
stream.write<uint32_t>(NULL);
|
stream.write<uint32_t>(NULL);
|
||||||
|
return object != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinarySerializer::save_object(const pclass::PropertyClass *object, BitStream &stream) const
|
void BinarySerializer::save_object(const pclass::PropertyClass *object, BitStream &stream) const
|
||||||
{
|
{
|
||||||
// Write any object headers
|
// Write any object headers
|
||||||
presave_object(object, stream);
|
if (!presave_object(object, stream))
|
||||||
|
|
||||||
// Make sure we have an object to write
|
|
||||||
if (!object)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remember where we started writing the object data
|
// Remember where we started writing the object data
|
||||||
|
@ -179,39 +177,17 @@ namespace serialization
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the property dynamic? (holding more than one value)
|
// Is the property dynamic? (holding more than one value)
|
||||||
auto &property_type = prop.get_type();
|
|
||||||
if (prop.is_dynamic())
|
if (prop.is_dynamic())
|
||||||
{
|
{
|
||||||
// Cast the property to a IDynamicProperty
|
// Cast the property to a IDynamicProperty
|
||||||
const auto &dynamic_property =
|
const auto &dynamic_property =
|
||||||
dynamic_cast<const pclass::IDynamicProperty &>(prop);
|
dynamic_cast<const pclass::IDynamicProperty &>(prop);
|
||||||
|
save_dynamic_property(dynamic_property, stream);
|
||||||
// Write the number of elements
|
|
||||||
stream.write<uint32_t>(dynamic_property.get_element_count());
|
|
||||||
|
|
||||||
// Iterate through the elements
|
|
||||||
for (auto i = 0; i < dynamic_property.get_element_count(); i++)
|
|
||||||
{
|
|
||||||
// Is this a collection of pointers?
|
|
||||||
if (prop.is_pointer())
|
|
||||||
{
|
|
||||||
// Is the property a collection of pointers to other objects?
|
|
||||||
if (property_type.get_kind() == pclass::Type::kind::CLASS)
|
|
||||||
// Write the value as a nested object
|
|
||||||
save_object(dynamic_property.get_object(i), stream);
|
|
||||||
else
|
|
||||||
// Write the value as normal (let the property deal with dereferencing)
|
|
||||||
dynamic_property.write_value_to(stream, i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// If the value isn't a pointer, and it's not dynamic, just write it as a value
|
|
||||||
dynamic_property.write_value_to(stream, i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (prop.is_pointer())
|
else if (prop.is_pointer())
|
||||||
{
|
{
|
||||||
// Does this property hold a pointer to another object?
|
// Does this property hold a pointer to another object?
|
||||||
if (property_type.get_kind() == pclass::Type::kind::CLASS)
|
if (prop.get_type().get_kind() == pclass::Type::kind::CLASS)
|
||||||
// Write the value as a nested object
|
// Write the value as a nested object
|
||||||
save_object(prop.get_object(), stream);
|
save_object(prop.get_object(), stream);
|
||||||
else
|
else
|
||||||
|
@ -236,6 +212,32 @@ namespace serialization
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BinarySerializer::save_dynamic_property(
|
||||||
|
const pclass::IDynamicProperty& prop, BitStream& stream) const
|
||||||
|
{
|
||||||
|
// Write the number of elements
|
||||||
|
stream.write<uint32_t>(prop.get_element_count());
|
||||||
|
|
||||||
|
// Iterate through the elements
|
||||||
|
for (auto i = 0; i < prop.get_element_count(); i++)
|
||||||
|
{
|
||||||
|
// Is this a collection of pointers?
|
||||||
|
if (prop.is_pointer())
|
||||||
|
{
|
||||||
|
// Is the property a collection of pointers to other objects?
|
||||||
|
if (prop.get_type().get_kind() == pclass::Type::kind::CLASS)
|
||||||
|
// Write the value as a nested object
|
||||||
|
save_object(prop.get_object(i), stream);
|
||||||
|
else
|
||||||
|
// Write the value as normal (let the property deal with dereferencing)
|
||||||
|
prop.write_value_to(stream, i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// If the value isn't a pointer, and it's not dynamic, just write it as a value
|
||||||
|
prop.write_value_to(stream, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BinarySerializer::load(
|
void BinarySerializer::load(
|
||||||
std::unique_ptr<pclass::PropertyClass> &dest,
|
std::unique_ptr<pclass::PropertyClass> &dest,
|
||||||
BitStream &stream, const std::size_t size)
|
BitStream &stream, const std::size_t size)
|
||||||
|
@ -378,43 +380,16 @@ namespace serialization
|
||||||
|
|
||||||
void BinarySerializer::load_property(pclass::IProperty &prop, BitStream &stream) const
|
void BinarySerializer::load_property(pclass::IProperty &prop, BitStream &stream) const
|
||||||
{
|
{
|
||||||
auto &property_type = prop.get_type();
|
|
||||||
if (prop.is_dynamic())
|
if (prop.is_dynamic())
|
||||||
{
|
{
|
||||||
auto &dynamic_property =
|
auto &dynamic_property =
|
||||||
dynamic_cast<pclass::IDynamicProperty &>(prop);
|
dynamic_cast<pclass::IDynamicProperty &>(prop);
|
||||||
|
load_dynamic_property(dynamic_property, stream);
|
||||||
// How many elements are there in this dynamic property?
|
|
||||||
const auto element_count = stream.read<uint32_t>();
|
|
||||||
dynamic_property.set_element_count(element_count);
|
|
||||||
|
|
||||||
// Load each of the elements
|
|
||||||
for (uint16_t i = 0; i < element_count; i++)
|
|
||||||
{
|
|
||||||
// Is this a collection of pointers?
|
|
||||||
if (dynamic_property.is_pointer())
|
|
||||||
{
|
|
||||||
// Is the property a collection of pointers to other objects?
|
|
||||||
if (property_type.get_kind() == pclass::Type::kind::CLASS)
|
|
||||||
{
|
|
||||||
// Read the object as a nested object
|
|
||||||
std::unique_ptr<pclass::PropertyClass> object = nullptr;
|
|
||||||
load_object(object, stream);
|
|
||||||
dynamic_property.set_object(object, i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// Read the value as normal (let the property deal with dereferencing)
|
|
||||||
dynamic_property.read_value_from(stream, i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// If the value isn't a pointer, and it's not dynamic, just read it as a value
|
|
||||||
dynamic_property.read_value_from(stream, i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (prop.is_pointer())
|
else if (prop.is_pointer())
|
||||||
{
|
{
|
||||||
// Does this property hold a pointer to another object?
|
// Does this property hold a pointer to another object?
|
||||||
if (property_type.get_kind() == pclass::Type::kind::CLASS)
|
if (prop.get_type().get_kind() == pclass::Type::kind::CLASS)
|
||||||
{
|
{
|
||||||
// Read the object as a nested object
|
// Read the object as a nested object
|
||||||
std::unique_ptr<pclass::PropertyClass> object = nullptr;
|
std::unique_ptr<pclass::PropertyClass> object = nullptr;
|
||||||
|
@ -429,5 +404,37 @@ namespace serialization
|
||||||
// If the value isn't a pointer, and it's not dynamic, just read it as a value
|
// If the value isn't a pointer, and it's not dynamic, just read it as a value
|
||||||
prop.read_value_from(stream);
|
prop.read_value_from(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BinarySerializer::load_dynamic_property(
|
||||||
|
pclass::IDynamicProperty& prop, BitStream& stream) const
|
||||||
|
{
|
||||||
|
// How many elements are there in this dynamic property?
|
||||||
|
const auto element_count = stream.read<uint32_t>();
|
||||||
|
prop.set_element_count(element_count);
|
||||||
|
|
||||||
|
// Load each of the elements
|
||||||
|
for (uint16_t i = 0; i < element_count; i++)
|
||||||
|
{
|
||||||
|
// Is this a collection of pointers?
|
||||||
|
if (prop.is_pointer())
|
||||||
|
{
|
||||||
|
// Is the property a collection of pointers to other objects?
|
||||||
|
if (prop.get_type().get_kind() == pclass::Type::kind::CLASS)
|
||||||
|
{
|
||||||
|
// Read the object as a nested object
|
||||||
|
std::unique_ptr<pclass::PropertyClass> object = nullptr;
|
||||||
|
load_object(object, stream);
|
||||||
|
prop.set_object(object, i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// Read the value as normal (let the property deal with dereferencing)
|
||||||
|
prop.read_value_from(stream, i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// If the value isn't a pointer, and it's not dynamic, just read it as a value
|
||||||
|
prop.read_value_from(stream, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
target_sources(${PROJECT_NAME}
|
target_sources(${PROJECT_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${PROJECT_SOURCE_DIR}/src/serialization/BinarySerializer.cpp
|
${PROJECT_SOURCE_DIR}/src/serialization/BinarySerializer.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/src/serialization/JsonSerializer.cpp
|
||||||
)
|
)
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include "ki/serialization/JsonSerializer.h"
|
||||||
|
|
||||||
|
using namespace nlohmann;
|
||||||
|
|
||||||
|
namespace ki
|
||||||
|
{
|
||||||
|
namespace serialization
|
||||||
|
{
|
||||||
|
JsonSerializer::JsonSerializer(pclass::TypeSystem& type_system,
|
||||||
|
const bool is_file)
|
||||||
|
{
|
||||||
|
m_type_system = &type_system;
|
||||||
|
m_is_file = is_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JsonSerializer::save(const pclass::PropertyClass* object) const
|
||||||
|
{
|
||||||
|
return save_object(object).dump(
|
||||||
|
m_is_file ? FILE_INDENT_VALUE : -1,
|
||||||
|
' ',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsonSerializer::presave_object(json& j, const pclass::PropertyClass* object) const
|
||||||
|
{
|
||||||
|
// Add the object's meta information
|
||||||
|
j["_pclass_meta"] = {
|
||||||
|
{ "type_hash", object ? object->get_type().get_hash() : NULL }
|
||||||
|
};
|
||||||
|
return object != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
json JsonSerializer::save_object(const pclass::PropertyClass* object) const
|
||||||
|
{
|
||||||
|
json j;
|
||||||
|
if (!presave_object(j, object))
|
||||||
|
return j;
|
||||||
|
|
||||||
|
// Add the object's properties
|
||||||
|
auto &property_list = object->get_properties();
|
||||||
|
for (auto it = property_list.begin();
|
||||||
|
it != property_list.end(); ++it)
|
||||||
|
{
|
||||||
|
auto &prop = *it;
|
||||||
|
save_property(j, prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonSerializer::save_property(json& j,
|
||||||
|
const pclass::IProperty &prop) const
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop.get_type().get_kind() == pclass::Type::kind::CLASS)
|
||||||
|
{
|
||||||
|
auto *other_object = prop.get_object();
|
||||||
|
j[prop.get_name()] = save_object(other_object);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto value = prop.get_value().dereference<json>();
|
||||||
|
j[prop.get_name()] = value.get<json>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonSerializer::save_dynamic_property(json& j,
|
||||||
|
const pclass::IDynamicProperty &prop) const
|
||||||
|
{
|
||||||
|
json property_value;
|
||||||
|
|
||||||
|
for (auto i = 0; i < prop.get_element_count(); ++i)
|
||||||
|
{
|
||||||
|
if (prop.get_type().get_kind() == pclass::Type::kind::CLASS)
|
||||||
|
{
|
||||||
|
auto *other_object = prop.get_object(i);
|
||||||
|
property_value.push_back(save_object(other_object));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto value = prop.get_value(i).dereference<json>();
|
||||||
|
property_value.push_back(value.get<json>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
j[prop.get_name()] = property_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,285 @@
|
||||||
|
{
|
||||||
|
"_pclass_meta": {
|
||||||
|
"type_hash": 2069042008
|
||||||
|
},
|
||||||
|
"collection": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
16,
|
||||||
|
17,
|
||||||
|
18,
|
||||||
|
19,
|
||||||
|
20,
|
||||||
|
21,
|
||||||
|
22,
|
||||||
|
23,
|
||||||
|
24,
|
||||||
|
25,
|
||||||
|
26,
|
||||||
|
27,
|
||||||
|
28,
|
||||||
|
29,
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
32,
|
||||||
|
33,
|
||||||
|
34,
|
||||||
|
35,
|
||||||
|
36,
|
||||||
|
37,
|
||||||
|
38,
|
||||||
|
39,
|
||||||
|
40,
|
||||||
|
41,
|
||||||
|
42,
|
||||||
|
43,
|
||||||
|
44,
|
||||||
|
45,
|
||||||
|
46,
|
||||||
|
47,
|
||||||
|
48,
|
||||||
|
49,
|
||||||
|
50,
|
||||||
|
51,
|
||||||
|
52,
|
||||||
|
53,
|
||||||
|
54,
|
||||||
|
55,
|
||||||
|
56,
|
||||||
|
57,
|
||||||
|
58,
|
||||||
|
59,
|
||||||
|
60,
|
||||||
|
61,
|
||||||
|
62,
|
||||||
|
63,
|
||||||
|
64,
|
||||||
|
65,
|
||||||
|
66,
|
||||||
|
67,
|
||||||
|
68,
|
||||||
|
69,
|
||||||
|
70,
|
||||||
|
71,
|
||||||
|
72,
|
||||||
|
73,
|
||||||
|
74,
|
||||||
|
75,
|
||||||
|
76,
|
||||||
|
77,
|
||||||
|
78,
|
||||||
|
79,
|
||||||
|
80,
|
||||||
|
81,
|
||||||
|
82,
|
||||||
|
83,
|
||||||
|
84,
|
||||||
|
85,
|
||||||
|
86,
|
||||||
|
87,
|
||||||
|
88,
|
||||||
|
89,
|
||||||
|
90,
|
||||||
|
91,
|
||||||
|
92,
|
||||||
|
93,
|
||||||
|
94,
|
||||||
|
95,
|
||||||
|
96,
|
||||||
|
97,
|
||||||
|
98,
|
||||||
|
99
|
||||||
|
],
|
||||||
|
"float32": 3.1415927410125732,
|
||||||
|
"float64": 3.141592653589793,
|
||||||
|
"int16": 515,
|
||||||
|
"int24": 263430,
|
||||||
|
"int32": 117967114,
|
||||||
|
"int4": -6,
|
||||||
|
"int64": 796025588171149586,
|
||||||
|
"int8": 1,
|
||||||
|
"int_ptr": 52,
|
||||||
|
"not_null_object": {
|
||||||
|
"_pclass_meta": {
|
||||||
|
"type_hash": 1181435066
|
||||||
|
},
|
||||||
|
"m_kind": 1
|
||||||
|
},
|
||||||
|
"null_object": {
|
||||||
|
"_pclass_meta": {
|
||||||
|
"type_hash": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"_pclass_meta": {
|
||||||
|
"type_hash": 1180894394
|
||||||
|
},
|
||||||
|
"extra_value": 10,
|
||||||
|
"m_kind": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_pclass_meta": {
|
||||||
|
"type_hash": 1180943546
|
||||||
|
},
|
||||||
|
"m_kind": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ptr_collection": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
16,
|
||||||
|
17,
|
||||||
|
18,
|
||||||
|
19,
|
||||||
|
20,
|
||||||
|
21,
|
||||||
|
22,
|
||||||
|
23,
|
||||||
|
24,
|
||||||
|
25,
|
||||||
|
26,
|
||||||
|
27,
|
||||||
|
28,
|
||||||
|
29,
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
32,
|
||||||
|
33,
|
||||||
|
34,
|
||||||
|
35,
|
||||||
|
36,
|
||||||
|
37,
|
||||||
|
38,
|
||||||
|
39,
|
||||||
|
40,
|
||||||
|
41,
|
||||||
|
42,
|
||||||
|
43,
|
||||||
|
44,
|
||||||
|
45,
|
||||||
|
46,
|
||||||
|
47,
|
||||||
|
48,
|
||||||
|
49,
|
||||||
|
50,
|
||||||
|
51,
|
||||||
|
52,
|
||||||
|
53,
|
||||||
|
54,
|
||||||
|
55,
|
||||||
|
56,
|
||||||
|
57,
|
||||||
|
58,
|
||||||
|
59,
|
||||||
|
60,
|
||||||
|
61,
|
||||||
|
62,
|
||||||
|
63,
|
||||||
|
64,
|
||||||
|
65,
|
||||||
|
66,
|
||||||
|
67,
|
||||||
|
68,
|
||||||
|
69,
|
||||||
|
70,
|
||||||
|
71,
|
||||||
|
72,
|
||||||
|
73,
|
||||||
|
74,
|
||||||
|
75,
|
||||||
|
76,
|
||||||
|
77,
|
||||||
|
78,
|
||||||
|
79,
|
||||||
|
80,
|
||||||
|
81,
|
||||||
|
82,
|
||||||
|
83,
|
||||||
|
84,
|
||||||
|
85,
|
||||||
|
86,
|
||||||
|
87,
|
||||||
|
88,
|
||||||
|
89,
|
||||||
|
90,
|
||||||
|
91,
|
||||||
|
92,
|
||||||
|
93,
|
||||||
|
94,
|
||||||
|
95,
|
||||||
|
96,
|
||||||
|
97,
|
||||||
|
98,
|
||||||
|
99
|
||||||
|
],
|
||||||
|
"string": "This is a test value",
|
||||||
|
"uint16": 515,
|
||||||
|
"uint24": 263430,
|
||||||
|
"uint32": 117967114,
|
||||||
|
"uint4": 5,
|
||||||
|
"uint64": 796025588171149586,
|
||||||
|
"uint8": 1,
|
||||||
|
"value_object": {
|
||||||
|
"_pclass_meta": {
|
||||||
|
"type_hash": 1180894394
|
||||||
|
},
|
||||||
|
"extra_value": 20,
|
||||||
|
"m_kind": 2
|
||||||
|
},
|
||||||
|
"vector3d": {
|
||||||
|
"x": 24.0,
|
||||||
|
"y": 61.0,
|
||||||
|
"z": 3.619999885559082
|
||||||
|
},
|
||||||
|
"wstring": [
|
||||||
|
84,
|
||||||
|
104,
|
||||||
|
105,
|
||||||
|
115,
|
||||||
|
32,
|
||||||
|
105,
|
||||||
|
115,
|
||||||
|
32,
|
||||||
|
97,
|
||||||
|
32,
|
||||||
|
116,
|
||||||
|
101,
|
||||||
|
115,
|
||||||
|
116,
|
||||||
|
32,
|
||||||
|
118,
|
||||||
|
97,
|
||||||
|
108,
|
||||||
|
117,
|
||||||
|
101
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
{"_pclass_meta":{"type_hash":2069042008},"collection":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],"float32":3.1415927410125732,"float64":3.141592653589793,"int16":515,"int24":263430,"int32":117967114,"int4":-6,"int64":796025588171149586,"int8":1,"int_ptr":52,"not_null_object":{"_pclass_meta":{"type_hash":1181435066},"m_kind":1},"null_object":{"_pclass_meta":{"type_hash":0}},"objects":[{"_pclass_meta":{"type_hash":1180894394},"extra_value":10,"m_kind":2},{"_pclass_meta":{"type_hash":1180943546},"m_kind":3}],"ptr_collection":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],"string":"This is a test value","uint16":515,"uint24":263430,"uint32":117967114,"uint4":5,"uint64":796025588171149586,"uint8":1,"value_object":{"_pclass_meta":{"type_hash":1180894394},"extra_value":20,"m_kind":2},"vector3d":{"x":24.0,"y":61.0,"z":3.619999885559082},"wstring":[84,104,105,115,32,105,115,32,97,32,116,101,115,116,32,118,97,108,117,101]}
|
|
@ -9,6 +9,7 @@
|
||||||
#include <ki/pclass/VectorProperty.h>
|
#include <ki/pclass/VectorProperty.h>
|
||||||
#include <ki/serialization/BinarySerializer.h>
|
#include <ki/serialization/BinarySerializer.h>
|
||||||
#include "ki/util/unique.h"
|
#include "ki/util/unique.h"
|
||||||
|
#include "ki/serialization/JsonSerializer.h"
|
||||||
|
|
||||||
using namespace ki;
|
using namespace ki;
|
||||||
|
|
||||||
|
@ -17,7 +18,11 @@ using namespace ki;
|
||||||
*/
|
*/
|
||||||
struct Vector3D
|
struct Vector3D
|
||||||
{
|
{
|
||||||
Vector3D(
|
// Allow json caster to access private members
|
||||||
|
friend pclass::detail::value_caster<Vector3D, nlohmann::json>;
|
||||||
|
friend pclass::detail::value_caster<nlohmann::json, Vector3D>;
|
||||||
|
|
||||||
|
explicit Vector3D(
|
||||||
const float x = 0.0f,
|
const float x = 0.0f,
|
||||||
const float y = 0.0f,
|
const float y = 0.0f,
|
||||||
const float z = 0.0f)
|
const float z = 0.0f)
|
||||||
|
@ -95,6 +100,45 @@ namespace detail
|
||||||
return Value::make_value<Vector3D>(value);
|
return Value::make_value<Vector3D>(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* value_caster specialization for casting Vector3D to json object.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
struct value_caster<Vector3D, nlohmann::json>
|
||||||
|
: value_caster_impl<Vector3D, nlohmann::json>
|
||||||
|
{
|
||||||
|
Value cast(const Value &value) const override
|
||||||
|
{
|
||||||
|
auto &vector = value.get<Vector3D>();
|
||||||
|
const nlohmann::json j = {
|
||||||
|
{ "x", vector.m_x },
|
||||||
|
{ "y", vector.m_y },
|
||||||
|
{ "z", vector.m_z }
|
||||||
|
};
|
||||||
|
return Value::make_value<nlohmann::json>(j);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* value_caster specialization for casting json object to Vector3D.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
struct value_caster<nlohmann::json, Vector3D>
|
||||||
|
: value_caster_impl<nlohmann::json, Vector3D>
|
||||||
|
{
|
||||||
|
Value cast(const Value &value) const override
|
||||||
|
{
|
||||||
|
auto &j = value.get<nlohmann::json>();
|
||||||
|
return Value::make_value<Vector3D>(
|
||||||
|
Vector3D(
|
||||||
|
j["x"].get<float>(),
|
||||||
|
j["y"].get<float>(),
|
||||||
|
j["z"].get<float>()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,6 +320,10 @@ void define_types()
|
||||||
g_type_system->define_class<NestedTestObjectA>("class NestedTestObjectA", nested_test_object);
|
g_type_system->define_class<NestedTestObjectA>("class NestedTestObjectA", nested_test_object);
|
||||||
g_type_system->define_class<NestedTestObjectB>("class NestedTestObjectB", nested_test_object);
|
g_type_system->define_class<NestedTestObjectB>("class NestedTestObjectB", nested_test_object);
|
||||||
g_type_system->define_class<TestObject>("class TestObject");
|
g_type_system->define_class<TestObject>("class TestObject");
|
||||||
|
|
||||||
|
pclass::ValueCaster::declare<Vector3D, nlohmann::json>();
|
||||||
|
pclass::ValueCaster::declare<nlohmann::json, Vector3D>();
|
||||||
|
|
||||||
g_types_defined = true;
|
g_types_defined = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,7 +455,7 @@ void test_serializer(
|
||||||
|
|
||||||
// Open the sample data
|
// Open the sample data
|
||||||
std::ifstream sample(
|
std::ifstream sample(
|
||||||
"samples/serialization_binary" + file_suffix + ".bin",
|
"samples/serialization/" + file_suffix + ".bin",
|
||||||
std::ios::binary
|
std::ios::binary
|
||||||
);
|
);
|
||||||
REQUIRE(sample.is_open());
|
REQUIRE(sample.is_open());
|
||||||
|
@ -468,6 +516,67 @@ void test_serializer(
|
||||||
delete[] sample_data;
|
delete[] sample_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conduct save/load tests with a BinarySerializer instance.
|
||||||
|
*/
|
||||||
|
void test_serializer(
|
||||||
|
std::unique_ptr<TestObject> &test_object,
|
||||||
|
serialization::JsonSerializer &serializer,
|
||||||
|
const std::string &file_suffix)
|
||||||
|
{
|
||||||
|
// Open the sample data
|
||||||
|
std::ifstream sample_file(
|
||||||
|
"samples/serialization/" + file_suffix + ".json",
|
||||||
|
std::ios::binary
|
||||||
|
);
|
||||||
|
REQUIRE(sample_file.is_open());
|
||||||
|
|
||||||
|
// Load the sample data into a buffer
|
||||||
|
const auto begin = sample_file.tellg();
|
||||||
|
sample_file.seekg(0, std::ios::end);
|
||||||
|
const auto end = sample_file.tellg();
|
||||||
|
const size_t sample_size = end - begin;
|
||||||
|
sample_file.seekg(std::ios::beg);
|
||||||
|
auto *sample_data = new char[sample_size];
|
||||||
|
sample_file.read(sample_data, sample_size);
|
||||||
|
sample_file.close();
|
||||||
|
|
||||||
|
// Load the sample data into a string
|
||||||
|
const auto sample = std::string(sample_data, sample_size);
|
||||||
|
delete[] sample_data;
|
||||||
|
|
||||||
|
SECTION("Saving objects")
|
||||||
|
{
|
||||||
|
// Create a test object, configure it, and write it to our stream
|
||||||
|
test_object = g_type_system->instantiate<TestObject>("class TestObject");
|
||||||
|
configure_test_object(*test_object);
|
||||||
|
const auto json_string = serializer.save(test_object.get());
|
||||||
|
|
||||||
|
// Delete the test object here so that it is not
|
||||||
|
// unnecessarily validated by the caller
|
||||||
|
test_object = nullptr;
|
||||||
|
|
||||||
|
// Validate the JSON string
|
||||||
|
REQUIRE(json_string == sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Loading objects")
|
||||||
|
{
|
||||||
|
// Load an object from the sample
|
||||||
|
std::unique_ptr<pclass::PropertyClass> object = nullptr;
|
||||||
|
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);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Serialization tests", "[serialization]")
|
TEST_CASE("Serialization tests", "[serialization]")
|
||||||
{
|
{
|
||||||
std::unique_ptr<TestObject> test_object = nullptr;
|
std::unique_ptr<TestObject> test_object = nullptr;
|
||||||
|
@ -481,7 +590,7 @@ TEST_CASE("Serialization tests", "[serialization]")
|
||||||
*g_type_system.get(), false,
|
*g_type_system.get(), false,
|
||||||
serialization::BinarySerializer::flags::NONE
|
serialization::BinarySerializer::flags::NONE
|
||||||
);
|
);
|
||||||
test_serializer(test_object, serializer, "_regular");
|
test_serializer(test_object, serializer, "regular");
|
||||||
}
|
}
|
||||||
SECTION("File format without compression")
|
SECTION("File format without compression")
|
||||||
{
|
{
|
||||||
|
@ -489,7 +598,7 @@ TEST_CASE("Serialization tests", "[serialization]")
|
||||||
*g_type_system.get(), true,
|
*g_type_system.get(), true,
|
||||||
serialization::BinarySerializer::flags::WRITE_SERIALIZER_FLAGS
|
serialization::BinarySerializer::flags::WRITE_SERIALIZER_FLAGS
|
||||||
);
|
);
|
||||||
test_serializer(test_object, serializer, "_file");
|
test_serializer(test_object, serializer, "file");
|
||||||
}
|
}
|
||||||
SECTION("Regular format with compression")
|
SECTION("Regular format with compression")
|
||||||
{
|
{
|
||||||
|
@ -497,7 +606,7 @@ TEST_CASE("Serialization tests", "[serialization]")
|
||||||
*g_type_system.get(), false,
|
*g_type_system.get(), false,
|
||||||
serialization::BinarySerializer::flags::COMPRESSED
|
serialization::BinarySerializer::flags::COMPRESSED
|
||||||
);
|
);
|
||||||
test_serializer(test_object, serializer, "_regular_compressed");
|
test_serializer(test_object, serializer, "regular_compressed");
|
||||||
}
|
}
|
||||||
SECTION("File format with compression")
|
SECTION("File format with compression")
|
||||||
{
|
{
|
||||||
|
@ -506,7 +615,22 @@ TEST_CASE("Serialization tests", "[serialization]")
|
||||||
serialization::BinarySerializer::flags::WRITE_SERIALIZER_FLAGS |
|
serialization::BinarySerializer::flags::WRITE_SERIALIZER_FLAGS |
|
||||||
serialization::BinarySerializer::flags::COMPRESSED
|
serialization::BinarySerializer::flags::COMPRESSED
|
||||||
);
|
);
|
||||||
test_serializer(test_object, serializer, "_file_compressed");
|
test_serializer(test_object, serializer, "file_compressed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("JsonSerializer")
|
||||||
|
{
|
||||||
|
SECTION("Regular format")
|
||||||
|
{
|
||||||
|
serialization::JsonSerializer serializer(*g_type_system, false);
|
||||||
|
test_serializer(test_object, serializer, "regular");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("File format")
|
||||||
|
{
|
||||||
|
serialization::JsonSerializer serializer(*g_type_system, true);
|
||||||
|
test_serializer(test_object, serializer, "file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue