mirror of https://github.com/SeanOMik/libki.git
serialization: Add FileSerializer and XmlSerializer
This commit is contained in:
parent
4833f7fb76
commit
69189e0fba
|
@ -5,6 +5,9 @@
|
||||||
#include "ki/pclass/Value.h"
|
#include "ki/pclass/Value.h"
|
||||||
#include "ki/util/BitTypes.h"
|
#include "ki/util/BitTypes.h"
|
||||||
#include "ki/pclass/EnumType.h"
|
#include "ki/pclass/EnumType.h"
|
||||||
|
#include <iomanip>
|
||||||
|
#include <locale>
|
||||||
|
#include <codecvt>
|
||||||
|
|
||||||
namespace ki
|
namespace ki
|
||||||
{
|
{
|
||||||
|
@ -138,6 +141,11 @@ namespace detail
|
||||||
{
|
{
|
||||||
using type = uint16_t;
|
using type = uint16_t;
|
||||||
};
|
};
|
||||||
|
template <>
|
||||||
|
struct string_cast_t<char16_t>
|
||||||
|
{
|
||||||
|
using type = int16_t;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enums should be written as 32-bit integers.
|
* Enums should be written as 32-bit integers.
|
||||||
|
@ -151,12 +159,45 @@ namespace detail
|
||||||
using type = enum_value_t;
|
using type = enum_value_t;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility to enforce that a template parameter has a minimum
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
template <uint8_t N, uint8_t MinN, typename Enable = void>
|
||||||
|
struct minimum
|
||||||
|
{
|
||||||
|
static constexpr uint8_t value = N;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <uint8_t N, uint8_t MinN>
|
||||||
|
struct minimum<
|
||||||
|
N, MinN,
|
||||||
|
typename std::enable_if<N < MinN>::type
|
||||||
|
>
|
||||||
|
{
|
||||||
|
static constexpr uint8_t value = MinN;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BitIntegers should be written as the most suitable integer type
|
||||||
|
* based on signedness and number of bits.
|
||||||
|
*/
|
||||||
|
template <uint8_t N, bool Unsigned>
|
||||||
|
struct string_cast_t<BitInteger<N, Unsigned>>
|
||||||
|
{
|
||||||
|
using type = typename BitInteger<
|
||||||
|
minimum<N, 16>::value, Unsigned>::type;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caster implementation for casting any type to string
|
* Caster implementation for casting any type to string
|
||||||
* via std::ostringstream.
|
* via std::ostringstream.
|
||||||
*/
|
*/
|
||||||
template <typename SrcT>
|
template <typename SrcT>
|
||||||
struct value_caster<SrcT, std::string>
|
struct value_caster<
|
||||||
|
SrcT, std::string,
|
||||||
|
typename std::enable_if<!std::is_floating_point<SrcT>::value>::type
|
||||||
|
>
|
||||||
: value_caster_impl<SrcT, std::string>
|
: value_caster_impl<SrcT, std::string>
|
||||||
{
|
{
|
||||||
std::string cast_value(const SrcT &value) const override
|
std::string cast_value(const SrcT &value) const override
|
||||||
|
@ -169,6 +210,90 @@ namespace detail
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caster implementation for casting floating point integers
|
||||||
|
* to string via std::ostringstream.
|
||||||
|
*/
|
||||||
|
template <typename SrcT>
|
||||||
|
struct value_caster<
|
||||||
|
SrcT, std::string,
|
||||||
|
typename std::enable_if<std::is_floating_point<SrcT>::value>::type
|
||||||
|
>
|
||||||
|
: value_caster_impl<SrcT, std::string>
|
||||||
|
{
|
||||||
|
std::string cast_value(const SrcT &value) const override
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << std::setprecision(std::numeric_limits<SrcT>::max_digits10) << value;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caster implementation for casting std::string to std::u16string
|
||||||
|
* via std::wstring_convert.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
struct value_caster<std::string, std::u16string>
|
||||||
|
: value_caster_impl<std::string, std::u16string>
|
||||||
|
{
|
||||||
|
std::u16string cast_value(const std::string &value) const override
|
||||||
|
{
|
||||||
|
#if _MSC_VER >= 1900
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<int16_t>, int16_t> convert;
|
||||||
|
auto converted_string = convert.from_bytes(value.data());
|
||||||
|
return std::u16string(
|
||||||
|
reinterpret_cast<const char16_t *>(converted_string.data()),
|
||||||
|
converted_string.size()
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||||
|
return convert.from_bytes(value.data());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caster implementation for casting std::u16string to std::string
|
||||||
|
* via std::wstring_convert.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
struct value_caster<std::u16string, std::string>
|
||||||
|
: value_caster_impl<std::u16string, std::string>
|
||||||
|
{
|
||||||
|
std::string cast_value(const std::u16string &value) const override
|
||||||
|
{
|
||||||
|
#if _MSC_VER >= 1900
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<int16_t>, int16_t> convert;
|
||||||
|
auto *p = reinterpret_cast<const int16_t *>(value.data());
|
||||||
|
return convert.to_bytes(p, p + value.size());
|
||||||
|
#else
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||||
|
return convert.to_bytes(value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caster implementation for casting from string to any type
|
||||||
|
* via std::istringstream.
|
||||||
|
*/
|
||||||
|
template <typename DestT>
|
||||||
|
struct value_caster<
|
||||||
|
std::string, DestT,
|
||||||
|
typename std::enable_if<!std::is_same<DestT, nlohmann::json>::value>::type
|
||||||
|
>
|
||||||
|
: value_caster_impl<std::string, DestT>
|
||||||
|
{
|
||||||
|
DestT cast_value(const std::string &value) const override
|
||||||
|
{
|
||||||
|
typename string_cast_t<DestT>::type casted_value;
|
||||||
|
std::istringstream iss(value);
|
||||||
|
iss >> casted_value;
|
||||||
|
return static_cast<DestT>(casted_value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caster implementation for casting from json to any
|
* Caster implementation for casting from json to any
|
||||||
* primitive type.
|
* primitive type.
|
||||||
|
@ -249,7 +374,15 @@ namespace detail
|
||||||
template <typename T, typename Enable = void>
|
template <typename T, typename Enable = void>
|
||||||
struct caster_declarer
|
struct caster_declarer
|
||||||
{
|
{
|
||||||
static void declare() {}
|
static void declare()
|
||||||
|
{
|
||||||
|
// These casters are required for JsonSerializer and
|
||||||
|
// XmlSerializer to work.
|
||||||
|
ValueCaster::declare<T, std::string>();
|
||||||
|
ValueCaster::declare<std::string, T>();
|
||||||
|
ValueCaster::declare<T, nlohmann::json>();
|
||||||
|
ValueCaster::declare<nlohmann::json, T>();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -280,7 +413,9 @@ namespace detail
|
||||||
ValueCaster::declare<T, float>();
|
ValueCaster::declare<T, float>();
|
||||||
ValueCaster::declare<T, double>();
|
ValueCaster::declare<T, double>();
|
||||||
ValueCaster::declare<T, std::string>();
|
ValueCaster::declare<T, std::string>();
|
||||||
|
ValueCaster::declare<std::string, T>();
|
||||||
ValueCaster::declare<T, nlohmann::json>();
|
ValueCaster::declare<T, nlohmann::json>();
|
||||||
|
ValueCaster::declare<nlohmann::json, T>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -312,7 +447,9 @@ namespace detail
|
||||||
ValueCaster::declare<T, float>();
|
ValueCaster::declare<T, float>();
|
||||||
ValueCaster::declare<T, double>();
|
ValueCaster::declare<T, double>();
|
||||||
ValueCaster::declare<T, std::string>();
|
ValueCaster::declare<T, std::string>();
|
||||||
|
ValueCaster::declare<std::string, T>();
|
||||||
ValueCaster::declare<T, nlohmann::json>();
|
ValueCaster::declare<T, nlohmann::json>();
|
||||||
|
ValueCaster::declare<nlohmann::json, T>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -342,7 +479,9 @@ namespace detail
|
||||||
ValueCaster::declare<T, uint32_t>();
|
ValueCaster::declare<T, uint32_t>();
|
||||||
ValueCaster::declare<T, uint64_t>();
|
ValueCaster::declare<T, uint64_t>();
|
||||||
ValueCaster::declare<T, std::string>();
|
ValueCaster::declare<T, std::string>();
|
||||||
|
ValueCaster::declare<std::string, T>();
|
||||||
ValueCaster::declare<T, nlohmann::json>();
|
ValueCaster::declare<T, nlohmann::json>();
|
||||||
|
ValueCaster::declare<nlohmann::json, T>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -355,8 +494,9 @@ namespace detail
|
||||||
{
|
{
|
||||||
static void declare()
|
static void declare()
|
||||||
{
|
{
|
||||||
// TODO: Casting string to u16string
|
ValueCaster::declare<std::string, std::u16string>();
|
||||||
ValueCaster::declare<std::string, nlohmann::json>();
|
ValueCaster::declare<std::string, nlohmann::json>();
|
||||||
|
ValueCaster::declare<nlohmann::json, std::string>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -369,8 +509,9 @@ namespace detail
|
||||||
{
|
{
|
||||||
static void declare()
|
static void declare()
|
||||||
{
|
{
|
||||||
// TODO: Casting u16string to string
|
ValueCaster::declare<std::u16string, std::string>();
|
||||||
ValueCaster::declare<std::u16string, nlohmann::json>();
|
ValueCaster::declare<std::u16string, nlohmann::json>();
|
||||||
|
ValueCaster::declare<nlohmann::json, std::u16string>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace pclass
|
||||||
public:
|
public:
|
||||||
explicit Element(const std::string &name, enum_value_t value);
|
explicit Element(const std::string &name, enum_value_t value);
|
||||||
|
|
||||||
std::string get_name() const;
|
const std::string &get_name() const;
|
||||||
enum_value_t get_value() const;
|
enum_value_t get_value() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -157,10 +157,10 @@ namespace pclass
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Value deref_value = value.dereference<ValueT>();
|
Value casted_value = value.as<ValueT>();
|
||||||
detail::primitive_type_helper<ValueT>::write_to(
|
detail::primitive_type_helper<ValueT>::write_to(
|
||||||
stream,
|
stream,
|
||||||
deref_value.get<ValueT>()
|
casted_value.get<ValueT>()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (runtime_error &e)
|
catch (runtime_error &e)
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace pclass
|
||||||
|
|
||||||
virtual ~IProperty() = default;
|
virtual ~IProperty() = default;
|
||||||
|
|
||||||
std::string get_name() const;
|
const std::string &get_name() const;
|
||||||
hash_t get_name_hash() const;
|
hash_t get_name_hash() const;
|
||||||
hash_t get_full_hash() const;
|
hash_t get_full_hash() const;
|
||||||
const Type &get_type() const;
|
const Type &get_type() const;
|
||||||
|
|
|
@ -21,6 +21,7 @@ namespace pclass
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit TypeSystem(std::unique_ptr<IHashCalculator> &hash_calculator);
|
explicit TypeSystem(std::unique_ptr<IHashCalculator> &hash_calculator);
|
||||||
|
virtual ~TypeSystem() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns The IHashCalculator instance this TypeSystem uses to calculate
|
* @returns The IHashCalculator instance this TypeSystem uses to calculate
|
||||||
|
@ -64,8 +65,6 @@ namespace pclass
|
||||||
PrimitiveType<ValueT> &define_primitive(const std::string &name)
|
PrimitiveType<ValueT> &define_primitive(const std::string &name)
|
||||||
{
|
{
|
||||||
detail::caster_declarer<ValueT>::declare();
|
detail::caster_declarer<ValueT>::declare();
|
||||||
ValueCaster::declare<nlohmann::json, ValueT>();
|
|
||||||
|
|
||||||
auto *type = new PrimitiveType<ValueT>(name, *this);
|
auto *type = new PrimitiveType<ValueT>(name, *this);
|
||||||
define_type(std::unique_ptr<Type>(
|
define_type(std::unique_ptr<Type>(
|
||||||
dynamic_cast<Type *>(type)
|
dynamic_cast<Type *>(type)
|
||||||
|
@ -116,8 +115,6 @@ namespace pclass
|
||||||
CppEnumType<EnumT> &define_enum(const std::string &name)
|
CppEnumType<EnumT> &define_enum(const std::string &name)
|
||||||
{
|
{
|
||||||
detail::caster_declarer<EnumT>::declare();
|
detail::caster_declarer<EnumT>::declare();
|
||||||
ValueCaster::declare<nlohmann::json, EnumT>();
|
|
||||||
|
|
||||||
auto *type = new CppEnumType<EnumT>(name, *this);
|
auto *type = new CppEnumType<EnumT>(name, *this);
|
||||||
define_type(std::unique_ptr<Type>(
|
define_type(std::unique_ptr<Type>(
|
||||||
dynamic_cast<Type *>(type)
|
dynamic_cast<Type *>(type)
|
||||||
|
@ -125,6 +122,12 @@ namespace pclass
|
||||||
return *type;
|
return *type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance of a PropertyClass-derived class.
|
||||||
|
* @param[in] name The name of the class type to instantiate.
|
||||||
|
*/
|
||||||
|
virtual std::unique_ptr<PropertyClass> instantiate(const std::string &name) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of a PropertyClass-derived class.
|
* Create a new instance of a PropertyClass-derived class.
|
||||||
* @tparam ClassT The expected compile-time class.
|
* @tparam ClassT The expected compile-time class.
|
||||||
|
@ -133,8 +136,7 @@ namespace pclass
|
||||||
template <typename ClassT>
|
template <typename ClassT>
|
||||||
std::unique_ptr<ClassT> instantiate(const std::string &name) const
|
std::unique_ptr<ClassT> instantiate(const std::string &name) const
|
||||||
{
|
{
|
||||||
const auto &type = get_type(name);
|
auto object = instantiate(name);
|
||||||
auto object = type.instantiate();
|
|
||||||
return std::unique_ptr<ClassT>(
|
return std::unique_ptr<ClassT>(
|
||||||
dynamic_cast<ClassT *>(object.release())
|
dynamic_cast<ClassT *>(object.release())
|
||||||
);
|
);
|
||||||
|
@ -153,8 +155,6 @@ namespace pclass
|
||||||
ClassType<ClassT> &define_class(
|
ClassType<ClassT> &define_class(
|
||||||
const std::string &name, const Type *base_class)
|
const std::string &name, const Type *base_class)
|
||||||
{
|
{
|
||||||
detail::caster_declarer<ClassT>::declare();
|
|
||||||
|
|
||||||
// If the caller does not specify a base class, automatically make
|
// If the caller does not specify a base class, automatically make
|
||||||
// ki::pclass::PropertyClass the base class (if it has been defined)
|
// ki::pclass::PropertyClass the base class (if it has been defined)
|
||||||
if (base_class == nullptr && has_type("class PropertyClass"))
|
if (base_class == nullptr && has_type("class PropertyClass"))
|
||||||
|
|
|
@ -222,6 +222,8 @@ namespace pclass
|
||||||
void add_caster()
|
void add_caster()
|
||||||
{
|
{
|
||||||
const auto dest_type_hash = typeid(DestT).hash_code();
|
const auto dest_type_hash = typeid(DestT).hash_code();
|
||||||
|
if (m_casts.find(dest_type_hash) != m_casts.end())
|
||||||
|
delete m_casts[dest_type_hash];
|
||||||
m_casts[dest_type_hash] = new detail::value_caster<SrcT, DestT>();
|
m_casts[dest_type_hash] = new detail::value_caster<SrcT, DestT>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,8 @@ namespace ki
|
||||||
namespace serialization
|
namespace serialization
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* TODO: Documentation
|
* A serializer class used to serialize PropertyClass instances into their binary
|
||||||
|
* representations, and deserialize binary data into PropertyClass instances.
|
||||||
*/
|
*/
|
||||||
class BinarySerializer
|
class BinarySerializer
|
||||||
{
|
{
|
||||||
|
@ -30,47 +31,50 @@ namespace serialization
|
||||||
WRITE_SERIALIZER_FLAGS = 0x01,
|
WRITE_SERIALIZER_FLAGS = 0x01,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When enabled, the serialized data (after the flags, if present) is compressed.
|
* When enabled, the serialized data (after the flags, if present) is
|
||||||
|
* potentially compressed. This is based on an added compression header.
|
||||||
*/
|
*/
|
||||||
COMPRESSED = 0x08
|
COMPRESSED = 0x08
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new binary serializer.
|
* Construct a new binary serializer.
|
||||||
* @param type_system The TypeSystem instance to acquire Type information from.
|
* @param[in] type_system The TypeSystem instance to acquire Type information from.
|
||||||
* @param is_file Determines whether or not to write type sizes, and property headers.
|
* @param[in] is_file Determines whether or not to write type sizes, and property headers.
|
||||||
* @param flags Determines how serialized data is formatted.
|
* @param[in] flags Determines how serialized data is formatted.
|
||||||
*/
|
*/
|
||||||
explicit BinarySerializer(const pclass::TypeSystem &type_system,
|
explicit BinarySerializer(const pclass::TypeSystem &type_system,
|
||||||
bool is_file, flags flags);
|
bool is_file, flags flags);
|
||||||
virtual ~BinarySerializer() {}
|
virtual ~BinarySerializer() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param object The object to write to the stream.
|
* Serialize an object into a BitStream.
|
||||||
* @param stream The stream to write the object to.
|
* @param[in] object The object to write to the stream.
|
||||||
|
* @param[in] stream The stream to write the object to.
|
||||||
*/
|
*/
|
||||||
void save(const pclass::PropertyClass *object, BitStream &stream);
|
void save(const pclass::PropertyClass *object, BitStream &stream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param dest Where to load the PropertyClass instance into.
|
* Deserialize the contents of a BitStream.
|
||||||
* @param stream The stream to read the object from.
|
* @param[out] dest Where to load the PropertyClass instance into.
|
||||||
* @param size The size of the stream's available data.
|
* @param[in] stream The stream to read the object from.
|
||||||
|
* @param[in] size The size of the stream's available data.
|
||||||
*/
|
*/
|
||||||
void load(std::unique_ptr<pclass::PropertyClass> &dest,
|
void load(std::unique_ptr<pclass::PropertyClass> &dest,
|
||||||
BitStream &stream, std::size_t size);
|
BitStream &stream, std::size_t size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @param object The object that is being saved.
|
* @param[in] object The object that is being saved.
|
||||||
* @param stream The stream to write the object header to.
|
* @param[in] stream The stream to write the object header to.
|
||||||
* @returns Whether or not the object is null.
|
* @returns Whether or not the object is null.
|
||||||
*/
|
*/
|
||||||
virtual bool presave_object(const pclass::PropertyClass *object, BitStream &stream) const;
|
virtual bool presave_object(const pclass::PropertyClass *object, BitStream &stream) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read an object header, and instantiate the necessary PropertyClass.
|
* Read an object header, and instantiate the necessary PropertyClass.
|
||||||
* @param dest Where to instantiate the PropertyClass.
|
* @param[out] dest Where to instantiate the PropertyClass.
|
||||||
* @param stream The stream to read the object header from.
|
* @param[in] stream The stream to read the object header from.
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
#include "ki/pclass/TypeSystem.h"
|
||||||
|
#include "ki/serialization/BinarySerializer.h"
|
||||||
|
|
||||||
|
namespace ki
|
||||||
|
{
|
||||||
|
namespace serialization
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* TODO: Documentation
|
||||||
|
*/
|
||||||
|
class FileSerializer
|
||||||
|
{
|
||||||
|
static constexpr const char *BINARY_HEADER = "BINd";
|
||||||
|
static constexpr const char *JSON_HEADER = "JSON";
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FileSerializer(pclass::TypeSystem &type_system);
|
||||||
|
|
||||||
|
void save_binary(pclass::PropertyClass *object,
|
||||||
|
BinarySerializer::flags flags, const std::string &filepath) const;
|
||||||
|
void save_xml(pclass::PropertyClass *object, const std::string &filepath) const;
|
||||||
|
void save_json(pclass::PropertyClass *object, const std::string &filepath) const;
|
||||||
|
|
||||||
|
void load(std::unique_ptr<pclass::PropertyClass> &dest, const std::string &filepath) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const pclass::TypeSystem *m_type_system;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,21 +16,23 @@ namespace serialization
|
||||||
static const int FILE_INDENT_VALUE = 2;
|
static const int FILE_INDENT_VALUE = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit JsonSerializer(pclass::TypeSystem &type_system, bool is_file);
|
explicit JsonSerializer(const pclass::TypeSystem &type_system, bool is_file);
|
||||||
virtual ~JsonSerializer() {}
|
virtual ~JsonSerializer() = default;
|
||||||
|
|
||||||
std::string save(const pclass::PropertyClass *object) const;
|
std::string save(const pclass::PropertyClass *object) const;
|
||||||
void load(std::unique_ptr<pclass::PropertyClass> &dest, const std::string &json_string) const;
|
void load(std::unique_ptr<pclass::PropertyClass> &dest, const std::string &json_string) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool presave_object(nlohmann::json &j, const pclass::PropertyClass *object) const;
|
||||||
|
virtual bool preload_object(std::unique_ptr<pclass::PropertyClass> &dest, nlohmann::json &j) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pclass::TypeSystem *m_type_system;
|
const pclass::TypeSystem *m_type_system;
|
||||||
bool m_is_file;
|
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;
|
nlohmann::json save_object(const pclass::PropertyClass *object) const;
|
||||||
void save_property(nlohmann::json &j, const pclass::IProperty &prop) const;
|
void save_property(nlohmann::json &j, const pclass::IProperty &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_object(std::unique_ptr<pclass::PropertyClass> &dest, nlohmann::json &j) const;
|
||||||
void load_property(pclass::IProperty &prop, nlohmann::json &j) const;
|
void load_property(pclass::IProperty &prop, nlohmann::json &j) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
#include <rapidxml.hpp>
|
||||||
|
#include "ki/pclass/TypeSystem.h"
|
||||||
|
|
||||||
|
namespace ki
|
||||||
|
{
|
||||||
|
namespace serialization
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* TODO: Documentation
|
||||||
|
*/
|
||||||
|
class XmlSerializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit XmlSerializer(const pclass::TypeSystem &type_system);
|
||||||
|
~XmlSerializer() = default;
|
||||||
|
|
||||||
|
std::string save(pclass::PropertyClass *object);
|
||||||
|
void load(std::unique_ptr<pclass::PropertyClass> &dest, const std::string &xml_string);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual rapidxml::xml_node<> *presave_object(const pclass::PropertyClass *object);
|
||||||
|
virtual void preload_object(std::unique_ptr<pclass::PropertyClass> &dest, rapidxml::xml_node<> *node);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const pclass::TypeSystem *m_type_system;
|
||||||
|
rapidxml::xml_document<> m_document;
|
||||||
|
|
||||||
|
void save_object(rapidxml::xml_node<> *root, const pclass::PropertyClass *object);
|
||||||
|
void save_property(rapidxml::xml_node<> *object, const pclass::IProperty &prop);
|
||||||
|
|
||||||
|
void load_object(std::unique_ptr<pclass::PropertyClass> &dest, rapidxml::xml_node<> *root);
|
||||||
|
void load_property(pclass::IProperty &prop, rapidxml::xml_node<> *node);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ namespace pclass
|
||||||
m_value = value;
|
m_value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EnumType::Element::get_name() const
|
const std::string &EnumType::Element::get_name() const
|
||||||
{
|
{
|
||||||
return m_name;
|
return m_name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace pclass
|
||||||
return *m_instance;
|
return *m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string IProperty::get_name() const
|
const std::string &IProperty::get_name() const
|
||||||
{
|
{
|
||||||
return m_name;
|
return m_name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,13 @@ namespace pclass
|
||||||
return *type;
|
return *type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<PropertyClass> TypeSystem::instantiate(
|
||||||
|
const std::string& name) const
|
||||||
|
{
|
||||||
|
const auto &type = get_type(name);
|
||||||
|
return type.instantiate();
|
||||||
|
}
|
||||||
|
|
||||||
void TypeSystem::define_type(std::unique_ptr<Type> type)
|
void TypeSystem::define_type(std::unique_ptr<Type> type)
|
||||||
{
|
{
|
||||||
// Does a type with this name already exist?
|
// Does a type with this name already exist?
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
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/FileSerializer.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/serialization/JsonSerializer.cpp
|
${PROJECT_SOURCE_DIR}/src/serialization/JsonSerializer.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/src/serialization/XmlSerializer.cpp
|
||||||
)
|
)
|
|
@ -0,0 +1,133 @@
|
||||||
|
#include "ki/serialization/FileSerializer.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include "ki/serialization/JsonSerializer.h"
|
||||||
|
#include "ki/serialization/XmlSerializer.h"
|
||||||
|
#include "rapidxml_utils.hpp"
|
||||||
|
|
||||||
|
namespace ki
|
||||||
|
{
|
||||||
|
namespace serialization
|
||||||
|
{
|
||||||
|
FileSerializer::FileSerializer(pclass::TypeSystem &type_system)
|
||||||
|
{
|
||||||
|
m_type_system = &type_system;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSerializer::save_binary(pclass::PropertyClass *object,
|
||||||
|
BinarySerializer::flags flags, const std::string &filepath) const
|
||||||
|
{
|
||||||
|
// Force the WRITE_SERIALIZER_FLAGS flag so that the correct flags
|
||||||
|
// can be loaded later
|
||||||
|
flags |= BinarySerializer::flags::WRITE_SERIALIZER_FLAGS;
|
||||||
|
|
||||||
|
// Create the buffer, stream, and serializer, and save the object
|
||||||
|
BitBuffer buffer;
|
||||||
|
BitStream stream(buffer);
|
||||||
|
BinarySerializer serializer(*m_type_system, true, flags);
|
||||||
|
serializer.save(object, stream);
|
||||||
|
|
||||||
|
// Write to file
|
||||||
|
std::ofstream ofs(filepath, std::ios::binary);
|
||||||
|
ofs << BINARY_HEADER;
|
||||||
|
ofs.write(
|
||||||
|
reinterpret_cast<char *>(buffer.data()),
|
||||||
|
stream.tell().as_bytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSerializer::save_xml(
|
||||||
|
pclass::PropertyClass *object, const std::string &filepath) const
|
||||||
|
{
|
||||||
|
// Serialize the object into an XML string
|
||||||
|
XmlSerializer serializer(*m_type_system);
|
||||||
|
const auto xml_string = serializer.save(object);
|
||||||
|
|
||||||
|
// Write the XML to a file
|
||||||
|
std::ofstream ofs(filepath, std::ios::binary);
|
||||||
|
if (!ofs.is_open())
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Failed to open filepath for writing: " << filepath;
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
ofs << R"(<?xml version="1.0" encoding="UTF-8"?>)" << std::endl
|
||||||
|
<< xml_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSerializer::save_json(
|
||||||
|
pclass::PropertyClass *object, const std::string &filepath) const
|
||||||
|
{
|
||||||
|
// Serialize the object into a JSON string
|
||||||
|
JsonSerializer serializer(*m_type_system, true);
|
||||||
|
const auto json_string = serializer.save(object);
|
||||||
|
std::ofstream ofs(filepath, std::ios::binary);
|
||||||
|
if (!ofs.is_open())
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Failed to open filepath for writing: " << filepath;
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
ofs << JSON_HEADER << json_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSerializer::load(
|
||||||
|
std::unique_ptr<pclass::PropertyClass> &dest, const std::string &filepath) const
|
||||||
|
{
|
||||||
|
// Open the specified file for reading
|
||||||
|
std::ifstream ifs(filepath, std::ios::binary | std::ios::ate);
|
||||||
|
if (!ifs.is_open())
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Failed to open filepath for reading: " << filepath;
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure there is at least enough data to determine which
|
||||||
|
// serializer was used
|
||||||
|
const size_t file_size = ifs.tellg();
|
||||||
|
ifs.seekg(std::ios::beg);
|
||||||
|
if (file_size < 4)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Not enough data to determine serializer used in file: "
|
||||||
|
<< filepath;
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the contents of the file into a buffer
|
||||||
|
std::vector<char> file_data(file_size);
|
||||||
|
ifs.read(file_data.data(), file_size);
|
||||||
|
ifs.close();
|
||||||
|
|
||||||
|
// Use the first 4 bytes to distinguish which serializer was used
|
||||||
|
if (strncmp(file_data.data(), BINARY_HEADER, 4) == 0)
|
||||||
|
{
|
||||||
|
// Create and populate a BitStream with the file's data
|
||||||
|
BitBuffer buffer(file_size - 4);
|
||||||
|
buffer.write_copy(
|
||||||
|
reinterpret_cast<uint8_t *>(&file_data[4]),
|
||||||
|
BitBuffer::buffer_pos(0, 0),
|
||||||
|
(file_size - 4) * 8
|
||||||
|
);
|
||||||
|
BitStream stream(buffer);
|
||||||
|
|
||||||
|
// Deserialize the contents of the stream
|
||||||
|
BinarySerializer serializer(*m_type_system, true,
|
||||||
|
BinarySerializer::flags::WRITE_SERIALIZER_FLAGS);
|
||||||
|
serializer.load(dest, stream, file_size - 4);
|
||||||
|
}
|
||||||
|
else if (strncmp(file_data.data(), JSON_HEADER, 4) == 0)
|
||||||
|
{
|
||||||
|
auto json_string = std::string(&file_data[4], file_size - 4);
|
||||||
|
JsonSerializer serializer(*m_type_system, true);
|
||||||
|
serializer.load(dest, json_string);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rapidxml::file<char> file(filepath.c_str());
|
||||||
|
XmlSerializer serializer(*m_type_system);
|
||||||
|
serializer.load(dest, file.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ namespace ki
|
||||||
{
|
{
|
||||||
namespace serialization
|
namespace serialization
|
||||||
{
|
{
|
||||||
JsonSerializer::JsonSerializer(pclass::TypeSystem& type_system,
|
JsonSerializer::JsonSerializer(const pclass::TypeSystem& type_system,
|
||||||
const bool is_file)
|
const bool is_file)
|
||||||
{
|
{
|
||||||
m_type_system = &type_system;
|
m_type_system = &type_system;
|
||||||
|
|
|
@ -0,0 +1,241 @@
|
||||||
|
#include "ki/serialization/XmlSerializer.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include <rapidxml_print.hpp>
|
||||||
|
|
||||||
|
namespace ki
|
||||||
|
{
|
||||||
|
namespace serialization
|
||||||
|
{
|
||||||
|
XmlSerializer::XmlSerializer(const pclass::TypeSystem &type_system)
|
||||||
|
{
|
||||||
|
m_type_system = &type_system;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string XmlSerializer::save(pclass::PropertyClass *object)
|
||||||
|
{
|
||||||
|
// Create the XML document and root node
|
||||||
|
m_document.clear();
|
||||||
|
auto *root_node = m_document.allocate_node(
|
||||||
|
rapidxml::node_type::node_element, "Objects");
|
||||||
|
m_document.append_node(root_node);
|
||||||
|
|
||||||
|
// Save the object into the root node
|
||||||
|
save_object(root_node, object);
|
||||||
|
|
||||||
|
// Print the XML document into the string and return it
|
||||||
|
std::string s;
|
||||||
|
print(std::back_inserter(s), m_document, 0);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
rapidxml::xml_node<> *XmlSerializer::presave_object(const pclass::PropertyClass *object)
|
||||||
|
{
|
||||||
|
if (!object)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto type_name = object->get_type().get_name();
|
||||||
|
auto *object_node = m_document.allocate_node(
|
||||||
|
rapidxml::node_type::node_element, "Class"
|
||||||
|
);
|
||||||
|
|
||||||
|
auto *class_attribute_value = m_document.allocate_string(type_name.data());
|
||||||
|
auto *class_attribute = m_document.allocate_attribute(
|
||||||
|
"Name", class_attribute_value
|
||||||
|
);
|
||||||
|
object_node->append_attribute(class_attribute);
|
||||||
|
return object_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmlSerializer::save_object(rapidxml::xml_node<> *root, const pclass::PropertyClass* object)
|
||||||
|
{
|
||||||
|
auto *object_node = presave_object(object);
|
||||||
|
if (!object_node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto &property_list = object->get_properties();
|
||||||
|
for (auto it = property_list.begin();
|
||||||
|
it != property_list.end(); ++it)
|
||||||
|
{
|
||||||
|
auto &prop = *it;
|
||||||
|
save_property(object_node, prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
root->append_node(object_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmlSerializer::save_property(rapidxml::xml_node<> *object, const pclass::IProperty& prop)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < prop.get_element_count(); ++i)
|
||||||
|
{
|
||||||
|
auto *property_name = m_document.allocate_string(prop.get_name().data());
|
||||||
|
auto *property_node = m_document.allocate_node(
|
||||||
|
rapidxml::node_element, property_name
|
||||||
|
);
|
||||||
|
|
||||||
|
if (prop.is_array())
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << i;
|
||||||
|
auto key = oss.str();
|
||||||
|
auto *key_value = m_document.allocate_string(key.data());
|
||||||
|
auto *key_attribute = m_document.allocate_attribute("key", key_value);
|
||||||
|
property_node->append_attribute(key_attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop.get_type().get_kind() == pclass::Type::Kind::CLASS)
|
||||||
|
{
|
||||||
|
auto *other_object = prop.get_object(i);
|
||||||
|
if (other_object)
|
||||||
|
save_object(property_node, other_object);
|
||||||
|
else
|
||||||
|
property_node->value("0");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto value = prop.get_value(i).as<std::string>().get<std::string>();
|
||||||
|
auto *property_value = m_document.allocate_string(value.data());
|
||||||
|
property_node->value(property_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
object->append_node(property_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmlSerializer::load(std::unique_ptr<pclass::PropertyClass> &dest, const std::string &xml_string)
|
||||||
|
{
|
||||||
|
m_document.clear();
|
||||||
|
auto *c_xml_string = m_document.allocate_string(xml_string.data());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_document.parse<0>(c_xml_string);
|
||||||
|
}
|
||||||
|
catch (rapidxml::parse_error &e)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Failed to parse given XML string: " << e.what();
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the <Objects> root node
|
||||||
|
auto *root_node = m_document.first_node("Objects");
|
||||||
|
load_object(dest, root_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmlSerializer::preload_object(std::unique_ptr<pclass::PropertyClass> &dest, rapidxml::xml_node<> *node)
|
||||||
|
{
|
||||||
|
auto *name_attribute = node->first_attribute("Name");
|
||||||
|
if (!name_attribute)
|
||||||
|
throw runtime_error("'Class' element was missing 'Name' attribute.");
|
||||||
|
|
||||||
|
const auto type_name = std::string(
|
||||||
|
name_attribute->value(), name_attribute->value_size());
|
||||||
|
const auto &type = m_type_system->get_type(type_name);
|
||||||
|
dest = type.instantiate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmlSerializer::load_object(
|
||||||
|
std::unique_ptr<pclass::PropertyClass> &dest, rapidxml::xml_node<> *root)
|
||||||
|
{
|
||||||
|
auto *object_node = root->first_node("Class");
|
||||||
|
if (!object_node)
|
||||||
|
{
|
||||||
|
dest = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
preload_object(dest, object_node);
|
||||||
|
auto &property_list = dest->get_properties();
|
||||||
|
for (auto it = property_list.begin();
|
||||||
|
it != property_list.end(); ++it)
|
||||||
|
{
|
||||||
|
auto &prop = *it;
|
||||||
|
load_property(prop, object_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All properties on this object have been set, let the new
|
||||||
|
// instance react to this change
|
||||||
|
dest->on_created();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmlSerializer::load_property(pclass::IProperty &prop, rapidxml::xml_node<> *node)
|
||||||
|
{
|
||||||
|
auto *property_name = prop.get_name().data();
|
||||||
|
|
||||||
|
// Get the node that contains data for this property and
|
||||||
|
// check if it exists (because it must exist at least once)
|
||||||
|
auto *property_node = node->first_node(property_name);
|
||||||
|
if (!property_node)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Missing parameter element: '" << prop.get_name() << "'.";
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of element nodes that have values for this property
|
||||||
|
// in the order of their "key" attribute
|
||||||
|
std::vector<rapidxml::xml_node<> *> property_entries;
|
||||||
|
while (property_node)
|
||||||
|
{
|
||||||
|
// Get the key attribute if the property is an array
|
||||||
|
if (prop.is_array())
|
||||||
|
{
|
||||||
|
auto *key_attribute = property_node->first_attribute("key");
|
||||||
|
if (!key_attribute)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Parameter element '" << prop.get_name()
|
||||||
|
<< "' is missing 'key' attribute.";
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the value of the key attribute
|
||||||
|
auto key_value_str = std::string(
|
||||||
|
key_attribute->value(), key_attribute->value_size()
|
||||||
|
);
|
||||||
|
std::size_t key_value;
|
||||||
|
std::istringstream iss(key_value_str);
|
||||||
|
iss >> key_value;
|
||||||
|
|
||||||
|
// Store this node at the key value
|
||||||
|
property_entries.insert(property_entries.begin() + key_value, property_node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
property_entries.push_back(property_node);
|
||||||
|
property_node = property_node->next_sibling(property_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the property is dynamic, make sure the element count has been set,
|
||||||
|
// otherwise, make sure we have the correct number of elements
|
||||||
|
if (prop.is_dynamic())
|
||||||
|
prop.set_element_count(property_entries.size());
|
||||||
|
else if (property_entries.size() != prop.get_element_count())
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Expected " << prop.get_element_count() << " values for '"
|
||||||
|
<< prop.get_name() << "' but got " << property_entries.size() << ".";
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the value(s) of the property
|
||||||
|
for (std::size_t i = 0; i < prop.get_element_count(); ++i)
|
||||||
|
{
|
||||||
|
auto *element_node = property_entries.at(i);
|
||||||
|
if (prop.get_type().get_kind() == pclass::Type::Kind::CLASS)
|
||||||
|
{
|
||||||
|
std::unique_ptr<pclass::PropertyClass> other_object = nullptr;
|
||||||
|
load_object(other_object, element_node);
|
||||||
|
prop.set_object(other_object, i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto element_value = std::string(
|
||||||
|
element_node->value(), element_node->value_size()
|
||||||
|
);
|
||||||
|
prop.set_value(
|
||||||
|
pclass::Value::make_reference<std::string>(element_value), i
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
|
@ -1,4 +1,4 @@
|
||||||
{
|
JSON{
|
||||||
"_pclass_meta": {
|
"_pclass_meta": {
|
||||||
"type_hash": 2069042008
|
"type_hash": 2069042008
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,257 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Objects>
|
||||||
|
<Class Name="class TestObject">
|
||||||
|
<int4>-6</int4>
|
||||||
|
<uint4>5</uint4>
|
||||||
|
<int8>1</int8>
|
||||||
|
<int16>515</int16>
|
||||||
|
<int24>263430</int24>
|
||||||
|
<int32>117967114</int32>
|
||||||
|
<int64>796025588171149586</int64>
|
||||||
|
<uint8>1</uint8>
|
||||||
|
<uint16>515</uint16>
|
||||||
|
<uint24>263430</uint24>
|
||||||
|
<uint32>117967114</uint32>
|
||||||
|
<uint64>796025588171149586</uint64>
|
||||||
|
<string>This is a test value</string>
|
||||||
|
<wstring>ᵗʰⁱˢ ⁱˢ ᵃ ᵗᵉˢᵗ ᵛᵃˡᵘᵉ</wstring>
|
||||||
|
<float32>3.14159274</float32>
|
||||||
|
<float64>3.1415926535897931</float64>
|
||||||
|
<vector3d>24 61 3.62</vector3d>
|
||||||
|
<int_ptr>52</int_ptr>
|
||||||
|
<int_array key="0">0</int_array>
|
||||||
|
<int_array key="1">1</int_array>
|
||||||
|
<int_array key="2">2</int_array>
|
||||||
|
<int_array key="3">3</int_array>
|
||||||
|
<int_array key="4">4</int_array>
|
||||||
|
<int_ptr_array key="0">0</int_ptr_array>
|
||||||
|
<int_ptr_array key="1">1</int_ptr_array>
|
||||||
|
<int_ptr_array key="2">2</int_ptr_array>
|
||||||
|
<int_ptr_array key="3">3</int_ptr_array>
|
||||||
|
<int_ptr_array key="4">4</int_ptr_array>
|
||||||
|
<object>
|
||||||
|
<Class Name="class NestedTestObjectA">
|
||||||
|
<m_kind>2</m_kind>
|
||||||
|
<extra_value>20</extra_value>
|
||||||
|
</Class>
|
||||||
|
</object>
|
||||||
|
<object_ptr>
|
||||||
|
<Class Name="class NestedTestObject">
|
||||||
|
<m_kind>1</m_kind>
|
||||||
|
</Class>
|
||||||
|
</object_ptr>
|
||||||
|
<null_object_ptr>0</null_object_ptr>
|
||||||
|
<int_vector key="0">0</int_vector>
|
||||||
|
<int_vector key="1">1</int_vector>
|
||||||
|
<int_vector key="2">2</int_vector>
|
||||||
|
<int_vector key="3">3</int_vector>
|
||||||
|
<int_vector key="4">4</int_vector>
|
||||||
|
<int_vector key="5">5</int_vector>
|
||||||
|
<int_vector key="6">6</int_vector>
|
||||||
|
<int_vector key="7">7</int_vector>
|
||||||
|
<int_vector key="8">8</int_vector>
|
||||||
|
<int_vector key="9">9</int_vector>
|
||||||
|
<int_vector key="10">10</int_vector>
|
||||||
|
<int_vector key="11">11</int_vector>
|
||||||
|
<int_vector key="12">12</int_vector>
|
||||||
|
<int_vector key="13">13</int_vector>
|
||||||
|
<int_vector key="14">14</int_vector>
|
||||||
|
<int_vector key="15">15</int_vector>
|
||||||
|
<int_vector key="16">16</int_vector>
|
||||||
|
<int_vector key="17">17</int_vector>
|
||||||
|
<int_vector key="18">18</int_vector>
|
||||||
|
<int_vector key="19">19</int_vector>
|
||||||
|
<int_vector key="20">20</int_vector>
|
||||||
|
<int_vector key="21">21</int_vector>
|
||||||
|
<int_vector key="22">22</int_vector>
|
||||||
|
<int_vector key="23">23</int_vector>
|
||||||
|
<int_vector key="24">24</int_vector>
|
||||||
|
<int_vector key="25">25</int_vector>
|
||||||
|
<int_vector key="26">26</int_vector>
|
||||||
|
<int_vector key="27">27</int_vector>
|
||||||
|
<int_vector key="28">28</int_vector>
|
||||||
|
<int_vector key="29">29</int_vector>
|
||||||
|
<int_vector key="30">30</int_vector>
|
||||||
|
<int_vector key="31">31</int_vector>
|
||||||
|
<int_vector key="32">32</int_vector>
|
||||||
|
<int_vector key="33">33</int_vector>
|
||||||
|
<int_vector key="34">34</int_vector>
|
||||||
|
<int_vector key="35">35</int_vector>
|
||||||
|
<int_vector key="36">36</int_vector>
|
||||||
|
<int_vector key="37">37</int_vector>
|
||||||
|
<int_vector key="38">38</int_vector>
|
||||||
|
<int_vector key="39">39</int_vector>
|
||||||
|
<int_vector key="40">40</int_vector>
|
||||||
|
<int_vector key="41">41</int_vector>
|
||||||
|
<int_vector key="42">42</int_vector>
|
||||||
|
<int_vector key="43">43</int_vector>
|
||||||
|
<int_vector key="44">44</int_vector>
|
||||||
|
<int_vector key="45">45</int_vector>
|
||||||
|
<int_vector key="46">46</int_vector>
|
||||||
|
<int_vector key="47">47</int_vector>
|
||||||
|
<int_vector key="48">48</int_vector>
|
||||||
|
<int_vector key="49">49</int_vector>
|
||||||
|
<int_vector key="50">50</int_vector>
|
||||||
|
<int_vector key="51">51</int_vector>
|
||||||
|
<int_vector key="52">52</int_vector>
|
||||||
|
<int_vector key="53">53</int_vector>
|
||||||
|
<int_vector key="54">54</int_vector>
|
||||||
|
<int_vector key="55">55</int_vector>
|
||||||
|
<int_vector key="56">56</int_vector>
|
||||||
|
<int_vector key="57">57</int_vector>
|
||||||
|
<int_vector key="58">58</int_vector>
|
||||||
|
<int_vector key="59">59</int_vector>
|
||||||
|
<int_vector key="60">60</int_vector>
|
||||||
|
<int_vector key="61">61</int_vector>
|
||||||
|
<int_vector key="62">62</int_vector>
|
||||||
|
<int_vector key="63">63</int_vector>
|
||||||
|
<int_vector key="64">64</int_vector>
|
||||||
|
<int_vector key="65">65</int_vector>
|
||||||
|
<int_vector key="66">66</int_vector>
|
||||||
|
<int_vector key="67">67</int_vector>
|
||||||
|
<int_vector key="68">68</int_vector>
|
||||||
|
<int_vector key="69">69</int_vector>
|
||||||
|
<int_vector key="70">70</int_vector>
|
||||||
|
<int_vector key="71">71</int_vector>
|
||||||
|
<int_vector key="72">72</int_vector>
|
||||||
|
<int_vector key="73">73</int_vector>
|
||||||
|
<int_vector key="74">74</int_vector>
|
||||||
|
<int_vector key="75">75</int_vector>
|
||||||
|
<int_vector key="76">76</int_vector>
|
||||||
|
<int_vector key="77">77</int_vector>
|
||||||
|
<int_vector key="78">78</int_vector>
|
||||||
|
<int_vector key="79">79</int_vector>
|
||||||
|
<int_vector key="80">80</int_vector>
|
||||||
|
<int_vector key="81">81</int_vector>
|
||||||
|
<int_vector key="82">82</int_vector>
|
||||||
|
<int_vector key="83">83</int_vector>
|
||||||
|
<int_vector key="84">84</int_vector>
|
||||||
|
<int_vector key="85">85</int_vector>
|
||||||
|
<int_vector key="86">86</int_vector>
|
||||||
|
<int_vector key="87">87</int_vector>
|
||||||
|
<int_vector key="88">88</int_vector>
|
||||||
|
<int_vector key="89">89</int_vector>
|
||||||
|
<int_vector key="90">90</int_vector>
|
||||||
|
<int_vector key="91">91</int_vector>
|
||||||
|
<int_vector key="92">92</int_vector>
|
||||||
|
<int_vector key="93">93</int_vector>
|
||||||
|
<int_vector key="94">94</int_vector>
|
||||||
|
<int_vector key="95">95</int_vector>
|
||||||
|
<int_vector key="96">96</int_vector>
|
||||||
|
<int_vector key="97">97</int_vector>
|
||||||
|
<int_vector key="98">98</int_vector>
|
||||||
|
<int_vector key="99">99</int_vector>
|
||||||
|
<int_ptr_vector key="0">0</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="1">1</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="2">2</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="3">3</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="4">4</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="5">5</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="6">6</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="7">7</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="8">8</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="9">9</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="10">10</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="11">11</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="12">12</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="13">13</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="14">14</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="15">15</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="16">16</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="17">17</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="18">18</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="19">19</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="20">20</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="21">21</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="22">22</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="23">23</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="24">24</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="25">25</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="26">26</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="27">27</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="28">28</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="29">29</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="30">30</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="31">31</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="32">32</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="33">33</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="34">34</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="35">35</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="36">36</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="37">37</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="38">38</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="39">39</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="40">40</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="41">41</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="42">42</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="43">43</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="44">44</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="45">45</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="46">46</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="47">47</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="48">48</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="49">49</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="50">50</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="51">51</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="52">52</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="53">53</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="54">54</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="55">55</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="56">56</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="57">57</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="58">58</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="59">59</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="60">60</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="61">61</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="62">62</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="63">63</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="64">64</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="65">65</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="66">66</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="67">67</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="68">68</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="69">69</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="70">70</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="71">71</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="72">72</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="73">73</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="74">74</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="75">75</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="76">76</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="77">77</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="78">78</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="79">79</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="80">80</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="81">81</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="82">82</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="83">83</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="84">84</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="85">85</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="86">86</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="87">87</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="88">88</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="89">89</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="90">90</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="91">91</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="92">92</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="93">93</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="94">94</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="95">95</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="96">96</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="97">97</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="98">98</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="99">99</int_ptr_vector>
|
||||||
|
<object_ptr_vector key="0">
|
||||||
|
<Class Name="class NestedTestObjectA">
|
||||||
|
<m_kind>2</m_kind>
|
||||||
|
<extra_value>10</extra_value>
|
||||||
|
</Class>
|
||||||
|
</object_ptr_vector>
|
||||||
|
<object_ptr_vector key="1">
|
||||||
|
<Class Name="class NestedTestObjectB">
|
||||||
|
<m_kind>3</m_kind>
|
||||||
|
</Class>
|
||||||
|
</object_ptr_vector>
|
||||||
|
</Class>
|
||||||
|
</Objects>
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,256 @@
|
||||||
|
<Objects>
|
||||||
|
<Class Name="class TestObject">
|
||||||
|
<int4>-6</int4>
|
||||||
|
<uint4>5</uint4>
|
||||||
|
<int8>1</int8>
|
||||||
|
<int16>515</int16>
|
||||||
|
<int24>263430</int24>
|
||||||
|
<int32>117967114</int32>
|
||||||
|
<int64>796025588171149586</int64>
|
||||||
|
<uint8>1</uint8>
|
||||||
|
<uint16>515</uint16>
|
||||||
|
<uint24>263430</uint24>
|
||||||
|
<uint32>117967114</uint32>
|
||||||
|
<uint64>796025588171149586</uint64>
|
||||||
|
<string>This is a test value</string>
|
||||||
|
<wstring>ᵗʰⁱˢ ⁱˢ ᵃ ᵗᵉˢᵗ ᵛᵃˡᵘᵉ</wstring>
|
||||||
|
<float32>3.14159274</float32>
|
||||||
|
<float64>3.1415926535897931</float64>
|
||||||
|
<vector3d>24 61 3.62</vector3d>
|
||||||
|
<int_ptr>52</int_ptr>
|
||||||
|
<int_array key="0">0</int_array>
|
||||||
|
<int_array key="1">1</int_array>
|
||||||
|
<int_array key="2">2</int_array>
|
||||||
|
<int_array key="3">3</int_array>
|
||||||
|
<int_array key="4">4</int_array>
|
||||||
|
<int_ptr_array key="0">0</int_ptr_array>
|
||||||
|
<int_ptr_array key="1">1</int_ptr_array>
|
||||||
|
<int_ptr_array key="2">2</int_ptr_array>
|
||||||
|
<int_ptr_array key="3">3</int_ptr_array>
|
||||||
|
<int_ptr_array key="4">4</int_ptr_array>
|
||||||
|
<object>
|
||||||
|
<Class Name="class NestedTestObjectA">
|
||||||
|
<m_kind>2</m_kind>
|
||||||
|
<extra_value>20</extra_value>
|
||||||
|
</Class>
|
||||||
|
</object>
|
||||||
|
<object_ptr>
|
||||||
|
<Class Name="class NestedTestObject">
|
||||||
|
<m_kind>1</m_kind>
|
||||||
|
</Class>
|
||||||
|
</object_ptr>
|
||||||
|
<null_object_ptr>0</null_object_ptr>
|
||||||
|
<int_vector key="0">0</int_vector>
|
||||||
|
<int_vector key="1">1</int_vector>
|
||||||
|
<int_vector key="2">2</int_vector>
|
||||||
|
<int_vector key="3">3</int_vector>
|
||||||
|
<int_vector key="4">4</int_vector>
|
||||||
|
<int_vector key="5">5</int_vector>
|
||||||
|
<int_vector key="6">6</int_vector>
|
||||||
|
<int_vector key="7">7</int_vector>
|
||||||
|
<int_vector key="8">8</int_vector>
|
||||||
|
<int_vector key="9">9</int_vector>
|
||||||
|
<int_vector key="10">10</int_vector>
|
||||||
|
<int_vector key="11">11</int_vector>
|
||||||
|
<int_vector key="12">12</int_vector>
|
||||||
|
<int_vector key="13">13</int_vector>
|
||||||
|
<int_vector key="14">14</int_vector>
|
||||||
|
<int_vector key="15">15</int_vector>
|
||||||
|
<int_vector key="16">16</int_vector>
|
||||||
|
<int_vector key="17">17</int_vector>
|
||||||
|
<int_vector key="18">18</int_vector>
|
||||||
|
<int_vector key="19">19</int_vector>
|
||||||
|
<int_vector key="20">20</int_vector>
|
||||||
|
<int_vector key="21">21</int_vector>
|
||||||
|
<int_vector key="22">22</int_vector>
|
||||||
|
<int_vector key="23">23</int_vector>
|
||||||
|
<int_vector key="24">24</int_vector>
|
||||||
|
<int_vector key="25">25</int_vector>
|
||||||
|
<int_vector key="26">26</int_vector>
|
||||||
|
<int_vector key="27">27</int_vector>
|
||||||
|
<int_vector key="28">28</int_vector>
|
||||||
|
<int_vector key="29">29</int_vector>
|
||||||
|
<int_vector key="30">30</int_vector>
|
||||||
|
<int_vector key="31">31</int_vector>
|
||||||
|
<int_vector key="32">32</int_vector>
|
||||||
|
<int_vector key="33">33</int_vector>
|
||||||
|
<int_vector key="34">34</int_vector>
|
||||||
|
<int_vector key="35">35</int_vector>
|
||||||
|
<int_vector key="36">36</int_vector>
|
||||||
|
<int_vector key="37">37</int_vector>
|
||||||
|
<int_vector key="38">38</int_vector>
|
||||||
|
<int_vector key="39">39</int_vector>
|
||||||
|
<int_vector key="40">40</int_vector>
|
||||||
|
<int_vector key="41">41</int_vector>
|
||||||
|
<int_vector key="42">42</int_vector>
|
||||||
|
<int_vector key="43">43</int_vector>
|
||||||
|
<int_vector key="44">44</int_vector>
|
||||||
|
<int_vector key="45">45</int_vector>
|
||||||
|
<int_vector key="46">46</int_vector>
|
||||||
|
<int_vector key="47">47</int_vector>
|
||||||
|
<int_vector key="48">48</int_vector>
|
||||||
|
<int_vector key="49">49</int_vector>
|
||||||
|
<int_vector key="50">50</int_vector>
|
||||||
|
<int_vector key="51">51</int_vector>
|
||||||
|
<int_vector key="52">52</int_vector>
|
||||||
|
<int_vector key="53">53</int_vector>
|
||||||
|
<int_vector key="54">54</int_vector>
|
||||||
|
<int_vector key="55">55</int_vector>
|
||||||
|
<int_vector key="56">56</int_vector>
|
||||||
|
<int_vector key="57">57</int_vector>
|
||||||
|
<int_vector key="58">58</int_vector>
|
||||||
|
<int_vector key="59">59</int_vector>
|
||||||
|
<int_vector key="60">60</int_vector>
|
||||||
|
<int_vector key="61">61</int_vector>
|
||||||
|
<int_vector key="62">62</int_vector>
|
||||||
|
<int_vector key="63">63</int_vector>
|
||||||
|
<int_vector key="64">64</int_vector>
|
||||||
|
<int_vector key="65">65</int_vector>
|
||||||
|
<int_vector key="66">66</int_vector>
|
||||||
|
<int_vector key="67">67</int_vector>
|
||||||
|
<int_vector key="68">68</int_vector>
|
||||||
|
<int_vector key="69">69</int_vector>
|
||||||
|
<int_vector key="70">70</int_vector>
|
||||||
|
<int_vector key="71">71</int_vector>
|
||||||
|
<int_vector key="72">72</int_vector>
|
||||||
|
<int_vector key="73">73</int_vector>
|
||||||
|
<int_vector key="74">74</int_vector>
|
||||||
|
<int_vector key="75">75</int_vector>
|
||||||
|
<int_vector key="76">76</int_vector>
|
||||||
|
<int_vector key="77">77</int_vector>
|
||||||
|
<int_vector key="78">78</int_vector>
|
||||||
|
<int_vector key="79">79</int_vector>
|
||||||
|
<int_vector key="80">80</int_vector>
|
||||||
|
<int_vector key="81">81</int_vector>
|
||||||
|
<int_vector key="82">82</int_vector>
|
||||||
|
<int_vector key="83">83</int_vector>
|
||||||
|
<int_vector key="84">84</int_vector>
|
||||||
|
<int_vector key="85">85</int_vector>
|
||||||
|
<int_vector key="86">86</int_vector>
|
||||||
|
<int_vector key="87">87</int_vector>
|
||||||
|
<int_vector key="88">88</int_vector>
|
||||||
|
<int_vector key="89">89</int_vector>
|
||||||
|
<int_vector key="90">90</int_vector>
|
||||||
|
<int_vector key="91">91</int_vector>
|
||||||
|
<int_vector key="92">92</int_vector>
|
||||||
|
<int_vector key="93">93</int_vector>
|
||||||
|
<int_vector key="94">94</int_vector>
|
||||||
|
<int_vector key="95">95</int_vector>
|
||||||
|
<int_vector key="96">96</int_vector>
|
||||||
|
<int_vector key="97">97</int_vector>
|
||||||
|
<int_vector key="98">98</int_vector>
|
||||||
|
<int_vector key="99">99</int_vector>
|
||||||
|
<int_ptr_vector key="0">0</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="1">1</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="2">2</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="3">3</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="4">4</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="5">5</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="6">6</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="7">7</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="8">8</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="9">9</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="10">10</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="11">11</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="12">12</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="13">13</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="14">14</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="15">15</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="16">16</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="17">17</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="18">18</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="19">19</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="20">20</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="21">21</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="22">22</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="23">23</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="24">24</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="25">25</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="26">26</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="27">27</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="28">28</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="29">29</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="30">30</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="31">31</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="32">32</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="33">33</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="34">34</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="35">35</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="36">36</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="37">37</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="38">38</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="39">39</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="40">40</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="41">41</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="42">42</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="43">43</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="44">44</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="45">45</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="46">46</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="47">47</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="48">48</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="49">49</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="50">50</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="51">51</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="52">52</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="53">53</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="54">54</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="55">55</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="56">56</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="57">57</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="58">58</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="59">59</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="60">60</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="61">61</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="62">62</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="63">63</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="64">64</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="65">65</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="66">66</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="67">67</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="68">68</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="69">69</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="70">70</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="71">71</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="72">72</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="73">73</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="74">74</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="75">75</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="76">76</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="77">77</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="78">78</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="79">79</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="80">80</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="81">81</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="82">82</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="83">83</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="84">84</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="85">85</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="86">86</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="87">87</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="88">88</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="89">89</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="90">90</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="91">91</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="92">92</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="93">93</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="94">94</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="95">95</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="96">96</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="97">97</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="98">98</int_ptr_vector>
|
||||||
|
<int_ptr_vector key="99">99</int_ptr_vector>
|
||||||
|
<object_ptr_vector key="0">
|
||||||
|
<Class Name="class NestedTestObjectA">
|
||||||
|
<m_kind>2</m_kind>
|
||||||
|
<extra_value>10</extra_value>
|
||||||
|
</Class>
|
||||||
|
</object_ptr_vector>
|
||||||
|
<object_ptr_vector key="1">
|
||||||
|
<Class Name="class NestedTestObjectB">
|
||||||
|
<m_kind>3</m_kind>
|
||||||
|
</Class>
|
||||||
|
</object_ptr_vector>
|
||||||
|
</Class>
|
||||||
|
</Objects>
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include <ki/serialization/BinarySerializer.h>
|
#include <ki/serialization/BinarySerializer.h>
|
||||||
#include <ki/serialization/JsonSerializer.h>
|
#include <ki/serialization/JsonSerializer.h>
|
||||||
#include "ki/pclass/Enum.h"
|
#include "ki/pclass/Enum.h"
|
||||||
|
#include "ki/serialization/XmlSerializer.h"
|
||||||
|
#include "ki/serialization/FileSerializer.h"
|
||||||
|
|
||||||
using namespace ki;
|
using namespace ki;
|
||||||
|
|
||||||
|
@ -23,14 +25,18 @@ struct Vector3D
|
||||||
friend pclass::detail::value_caster<Vector3D, nlohmann::json>;
|
friend pclass::detail::value_caster<Vector3D, nlohmann::json>;
|
||||||
friend pclass::detail::value_caster<nlohmann::json, Vector3D>;
|
friend pclass::detail::value_caster<nlohmann::json, Vector3D>;
|
||||||
|
|
||||||
|
// Allow string caster to access private members
|
||||||
|
friend pclass::detail::value_caster<Vector3D, std::string>;
|
||||||
|
friend pclass::detail::value_caster<std::string, Vector3D>;
|
||||||
|
|
||||||
explicit 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)
|
||||||
{
|
{
|
||||||
this->m_x = x;
|
m_x = x;
|
||||||
this->m_y = y;
|
m_y = y;
|
||||||
this->m_z = z;
|
m_z = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3D &operator=(const Vector3D &that)
|
Vector3D &operator=(const Vector3D &that)
|
||||||
|
@ -133,6 +139,41 @@ namespace detail
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* value_caster specialization for casting Vector3D to std::string.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
struct value_caster<Vector3D, std::string>
|
||||||
|
: value_caster_impl<Vector3D, std::string>
|
||||||
|
{
|
||||||
|
std::string cast_value(const Vector3D &value) const override
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << value.m_x << " "
|
||||||
|
<< value.m_y << " "
|
||||||
|
<< value.m_z;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* value_caster specialization for casting std::string to Vector3D.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
struct value_caster<std::string, Vector3D>
|
||||||
|
: value_caster_impl<std::string, Vector3D>
|
||||||
|
{
|
||||||
|
Vector3D cast_value(const std::string &value) const override
|
||||||
|
{
|
||||||
|
Vector3D result;
|
||||||
|
std::istringstream iss(value);
|
||||||
|
iss >> result.m_x;
|
||||||
|
iss >> result.m_y;
|
||||||
|
iss >> result.m_z;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -465,7 +506,7 @@ void validate_test_object(TestObject &object)
|
||||||
*/
|
*/
|
||||||
void test_serializer(
|
void test_serializer(
|
||||||
std::unique_ptr<TestObject> &test_object,
|
std::unique_ptr<TestObject> &test_object,
|
||||||
serialization::BinarySerializer &serializer,
|
serialization::BinarySerializer::flags flags,
|
||||||
const std::string &file_suffix)
|
const std::string &file_suffix)
|
||||||
{
|
{
|
||||||
BitBuffer buffer;
|
BitBuffer buffer;
|
||||||
|
@ -475,20 +516,18 @@ void test_serializer(
|
||||||
// Open the sample data
|
// Open the sample data
|
||||||
std::ifstream sample(
|
std::ifstream sample(
|
||||||
"samples/serialization/" + file_suffix + ".bin",
|
"samples/serialization/" + file_suffix + ".bin",
|
||||||
std::ios::binary
|
std::ios::binary | std::ios::ate
|
||||||
);
|
);
|
||||||
REQUIRE(sample.is_open());
|
REQUIRE(sample.is_open());
|
||||||
|
|
||||||
// Load the sample data
|
// Load the sample data
|
||||||
const auto begin = sample.tellg();
|
const size_t sample_size = sample.tellg();
|
||||||
sample.seekg(0, std::ios::end);
|
|
||||||
const auto end = sample.tellg();
|
|
||||||
const size_t sample_size = end - begin;
|
|
||||||
sample.seekg(std::ios::beg);
|
sample.seekg(std::ios::beg);
|
||||||
auto *sample_data = new char[sample_size];
|
auto *sample_data = new char[sample_size];
|
||||||
sample.read(sample_data, sample_size);
|
sample.read(sample_data, sample_size);
|
||||||
sample.close();
|
sample.close();
|
||||||
|
|
||||||
|
serialization::BinarySerializer serializer(*g_type_system, false, flags);
|
||||||
SECTION("Saving objects")
|
SECTION("Saving objects")
|
||||||
{
|
{
|
||||||
// Create a test object, configure it, and write it to our stream
|
// Create a test object, configure it, and write it to our stream
|
||||||
|
@ -512,7 +551,6 @@ void test_serializer(
|
||||||
// Cleanup
|
// Cleanup
|
||||||
delete[] stream_data;
|
delete[] stream_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Loading objects")
|
SECTION("Loading objects")
|
||||||
{
|
{
|
||||||
// Write the sample data to the bit stream
|
// Write the sample data to the bit stream
|
||||||
|
@ -536,61 +574,96 @@ void test_serializer(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conduct save/load tests with a BinarySerializer instance.
|
*
|
||||||
*/
|
*/
|
||||||
void test_serializer(
|
void test_file_serializer_load(
|
||||||
std::unique_ptr<TestObject> &test_object,
|
std::unique_ptr<TestObject> &test_object,
|
||||||
serialization::JsonSerializer &serializer,
|
const std::string &filename)
|
||||||
const std::string &file_suffix)
|
|
||||||
{
|
{
|
||||||
// Open the sample data
|
serialization::FileSerializer serializer(*g_type_system);
|
||||||
std::ifstream sample_file(
|
|
||||||
"samples/serialization/" + file_suffix + ".json",
|
// Load an object from a pre-made sample
|
||||||
std::ios::binary
|
const auto sample_filepath = "samples/serialization/" + filename;
|
||||||
|
std::unique_ptr<pclass::PropertyClass> object = nullptr;
|
||||||
|
serializer.load(object, sample_filepath);
|
||||||
|
|
||||||
|
// 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(sample_file.is_open());
|
REQUIRE(test_object != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// 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();
|
void test_file_serializer_save(
|
||||||
const size_t sample_size = end - begin;
|
const std::string &out_filepath,
|
||||||
sample_file.seekg(std::ios::beg);
|
const std::string &sample_filepath)
|
||||||
|
{
|
||||||
|
// Load sample data
|
||||||
|
std::ifstream sample_ifs(
|
||||||
|
sample_filepath,
|
||||||
|
std::ios::binary | std::ios::ate
|
||||||
|
);
|
||||||
|
REQUIRE(sample_ifs.is_open());
|
||||||
|
const auto sample_size = sample_ifs.tellg();
|
||||||
|
sample_ifs.seekg(std::ios::beg);
|
||||||
auto *sample_data = new char[sample_size];
|
auto *sample_data = new char[sample_size];
|
||||||
sample_file.read(sample_data, sample_size);
|
sample_ifs.read(sample_data, sample_size);
|
||||||
sample_file.close();
|
sample_ifs.close();
|
||||||
|
|
||||||
// Load the sample data into a string
|
// Load output file that was just created
|
||||||
const auto sample = std::string(sample_data, sample_size);
|
std::ifstream output_ifs(
|
||||||
|
out_filepath,
|
||||||
|
std::ios::binary | std::ios::ate
|
||||||
|
);
|
||||||
|
REQUIRE(output_ifs.is_open());
|
||||||
|
const auto output_size = output_ifs.tellg();
|
||||||
|
output_ifs.seekg(std::ios::beg);
|
||||||
|
auto *output_data = new char[output_size];
|
||||||
|
output_ifs.read(output_data, output_size);
|
||||||
|
output_ifs.close();
|
||||||
|
|
||||||
|
// Validate the output
|
||||||
|
REQUIRE(sample_size == output_size);
|
||||||
|
REQUIRE(strncmp(sample_data, output_data, sample_size) == 0);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
delete[] sample_data;
|
delete[] sample_data;
|
||||||
|
delete[] output_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void test_binary_file_serializer(
|
||||||
|
std::unique_ptr<TestObject> &test_object,
|
||||||
|
const serialization::BinarySerializer::flags flags,
|
||||||
|
const std::string &filename)
|
||||||
|
{
|
||||||
SECTION("Saving objects")
|
SECTION("Saving objects")
|
||||||
{
|
{
|
||||||
// Create a test object, configure it, and write it to our stream
|
// Create a test object, configure it, and write it a file
|
||||||
|
serialization::FileSerializer serializer(*g_type_system);
|
||||||
test_object = g_type_system->instantiate<TestObject>("class TestObject");
|
test_object = g_type_system->instantiate<TestObject>("class TestObject");
|
||||||
configure_test_object(*test_object);
|
configure_test_object(*test_object);
|
||||||
const auto json_string = serializer.save(test_object.get());
|
const auto out_filepath = "out_" + filename + ".bin";
|
||||||
|
serializer.save_binary(test_object.get(), flags, out_filepath);
|
||||||
|
test_file_serializer_save(
|
||||||
|
out_filepath,
|
||||||
|
"samples/serialization/" + filename + ".bin"
|
||||||
|
);
|
||||||
|
|
||||||
// Delete the test object here so that it is not
|
// Delete the test object here so that it is not
|
||||||
// unnecessarily validated by the caller
|
// unnecessarily validated by the caller
|
||||||
test_object = nullptr;
|
test_object = nullptr;
|
||||||
|
|
||||||
// Validate the JSON string
|
|
||||||
REQUIRE(json_string == sample);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Loading objects")
|
SECTION("Loading objects")
|
||||||
{
|
{
|
||||||
// Load an object from the sample
|
test_file_serializer_load(
|
||||||
std::unique_ptr<pclass::PropertyClass> object = nullptr;
|
test_object, filename + ".bin");
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,53 +674,189 @@ TEST_CASE("Serialization tests", "[serialization]")
|
||||||
|
|
||||||
SECTION("BinarySerializer")
|
SECTION("BinarySerializer")
|
||||||
{
|
{
|
||||||
SECTION("Regular format without compression")
|
SECTION("Without compression")
|
||||||
{
|
{
|
||||||
serialization::BinarySerializer serializer(
|
test_serializer(
|
||||||
*g_type_system, false,
|
test_object,
|
||||||
serialization::BinarySerializer::flags::NONE
|
serialization::BinarySerializer::flags::NONE,
|
||||||
|
"regular"
|
||||||
);
|
);
|
||||||
test_serializer(test_object, serializer, "regular");
|
|
||||||
}
|
}
|
||||||
SECTION("File format without compression")
|
SECTION("With compression")
|
||||||
{
|
{
|
||||||
serialization::BinarySerializer serializer(
|
test_serializer(
|
||||||
*g_type_system, true,
|
test_object,
|
||||||
serialization::BinarySerializer::flags::WRITE_SERIALIZER_FLAGS
|
serialization::BinarySerializer::flags::COMPRESSED,
|
||||||
|
"regular_compressed"
|
||||||
);
|
);
|
||||||
test_serializer(test_object, serializer, "file");
|
|
||||||
}
|
|
||||||
SECTION("Regular format with compression")
|
|
||||||
{
|
|
||||||
serialization::BinarySerializer serializer(
|
|
||||||
*g_type_system, false,
|
|
||||||
serialization::BinarySerializer::flags::COMPRESSED
|
|
||||||
);
|
|
||||||
test_serializer(test_object, serializer, "regular_compressed");
|
|
||||||
}
|
|
||||||
SECTION("File format with compression")
|
|
||||||
{
|
|
||||||
serialization::BinarySerializer serializer(
|
|
||||||
*g_type_system, true,
|
|
||||||
serialization::BinarySerializer::flags::WRITE_SERIALIZER_FLAGS |
|
|
||||||
serialization::BinarySerializer::flags::COMPRESSED
|
|
||||||
);
|
|
||||||
test_serializer(test_object, serializer, "file_compressed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("JsonSerializer")
|
SECTION("JsonSerializer")
|
||||||
{
|
{
|
||||||
SECTION("Regular format")
|
// Open the sample data
|
||||||
{
|
std::ifstream sample_file(
|
||||||
serialization::JsonSerializer serializer(*g_type_system, false);
|
"samples/serialization/regular.json",
|
||||||
test_serializer(test_object, serializer, "regular");
|
std::ios::binary | std::ios::ate
|
||||||
}
|
);
|
||||||
|
REQUIRE(sample_file.is_open());
|
||||||
|
|
||||||
SECTION("File format")
|
// Load the sample data into a buffer
|
||||||
|
const size_t sample_size = sample_file.tellg();
|
||||||
|
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;
|
||||||
|
|
||||||
|
serialization::JsonSerializer serializer(*g_type_system, false);
|
||||||
|
SECTION("Saving objects")
|
||||||
{
|
{
|
||||||
serialization::JsonSerializer serializer(*g_type_system, true);
|
// Create a test object, configure it, and write it to our stream
|
||||||
test_serializer(test_object, serializer, "file");
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("XmlSerializer")
|
||||||
|
{
|
||||||
|
// Open the sample data
|
||||||
|
std::ifstream sample_file(
|
||||||
|
"samples/serialization/regular.xml",
|
||||||
|
std::ios::binary | std::ios::ate
|
||||||
|
);
|
||||||
|
REQUIRE(sample_file.is_open());
|
||||||
|
|
||||||
|
// Load the sample data into a buffer
|
||||||
|
const size_t sample_size = sample_file.tellg();
|
||||||
|
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
|
||||||
|
auto sample = std::string(sample_data, sample_size);
|
||||||
|
delete[] sample_data;
|
||||||
|
|
||||||
|
serialization::XmlSerializer serializer(*g_type_system);
|
||||||
|
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);
|
||||||
|
auto xml_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(xml_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("FileSerializer")
|
||||||
|
{
|
||||||
|
SECTION("Binary")
|
||||||
|
{
|
||||||
|
test_binary_file_serializer(
|
||||||
|
test_object,
|
||||||
|
serialization::BinarySerializer::flags::NONE,
|
||||||
|
"file"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SECTION("Compressed Binary")
|
||||||
|
{
|
||||||
|
test_binary_file_serializer(
|
||||||
|
test_object,
|
||||||
|
serialization::BinarySerializer::flags::COMPRESSED,
|
||||||
|
"file_compressed"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SECTION("JSON")
|
||||||
|
{
|
||||||
|
SECTION("Saving objects")
|
||||||
|
{
|
||||||
|
// Create a test object, configure it, and write it a file
|
||||||
|
serialization::FileSerializer serializer(*g_type_system);
|
||||||
|
test_object = g_type_system->instantiate<TestObject>("class TestObject");
|
||||||
|
configure_test_object(*test_object);
|
||||||
|
const auto out_filepath = "out_file.json";
|
||||||
|
serializer.save_json(test_object.get(), out_filepath);
|
||||||
|
test_file_serializer_save(
|
||||||
|
out_filepath,
|
||||||
|
"samples/serialization/file.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete the test object here so that it is not
|
||||||
|
// unnecessarily validated by the caller
|
||||||
|
test_object = nullptr;
|
||||||
|
}
|
||||||
|
SECTION("Loading objects")
|
||||||
|
{
|
||||||
|
test_file_serializer_load(test_object, "file.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SECTION("XML")
|
||||||
|
{
|
||||||
|
SECTION("Saving objects")
|
||||||
|
{
|
||||||
|
// Create a test object, configure it, and write it a file
|
||||||
|
serialization::FileSerializer serializer(*g_type_system);
|
||||||
|
test_object = g_type_system->instantiate<TestObject>("class TestObject");
|
||||||
|
configure_test_object(*test_object);
|
||||||
|
const auto out_filepath = "out_file.xml";
|
||||||
|
serializer.save_xml(test_object.get(), out_filepath);
|
||||||
|
test_file_serializer_save(
|
||||||
|
out_filepath,
|
||||||
|
"samples/serialization/file.xml"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete the test object here so that it is not
|
||||||
|
// unnecessarily validated by the caller
|
||||||
|
test_object = nullptr;
|
||||||
|
}
|
||||||
|
SECTION("Loading objects")
|
||||||
|
{
|
||||||
|
test_file_serializer_load(test_object, "file.xml");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,448 @@
|
||||||
|
#ifndef RAPIDXML_PRINT_HPP_INCLUDED
|
||||||
|
#define RAPIDXML_PRINT_HPP_INCLUDED
|
||||||
|
|
||||||
|
// Copyright (C) 2006, 2009 Marcin Kalicinski
|
||||||
|
// Version 1.13
|
||||||
|
// Revision $DateTime: 2009/05/13 01:46:17 $
|
||||||
|
//! \file rapidxml_print.hpp This file contains rapidxml printer implementation
|
||||||
|
|
||||||
|
#include "rapidxml.hpp"
|
||||||
|
|
||||||
|
// Only include streams if not disabled
|
||||||
|
#ifndef RAPIDXML_NO_STREAMS
|
||||||
|
#include <ostream>
|
||||||
|
#include <iterator>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rapidxml
|
||||||
|
{
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
// Printing flags
|
||||||
|
|
||||||
|
const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function.
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
// Internal
|
||||||
|
|
||||||
|
//! \cond internal
|
||||||
|
namespace internal
|
||||||
|
{
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Internal character operations
|
||||||
|
|
||||||
|
// Copy characters from given range to given output iterator
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out)
|
||||||
|
{
|
||||||
|
while (begin != end)
|
||||||
|
*out++ = *begin++;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy characters from given range to given output iterator and expand
|
||||||
|
// characters into references (< > ' " &)
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out)
|
||||||
|
{
|
||||||
|
while (begin != end)
|
||||||
|
{
|
||||||
|
if (*begin == noexpand)
|
||||||
|
{
|
||||||
|
*out++ = *begin; // No expansion, copy character
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (*begin)
|
||||||
|
{
|
||||||
|
case Ch('<'):
|
||||||
|
*out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';');
|
||||||
|
break;
|
||||||
|
case Ch('>'):
|
||||||
|
*out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';');
|
||||||
|
break;
|
||||||
|
case Ch('\''):
|
||||||
|
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';');
|
||||||
|
break;
|
||||||
|
case Ch('"'):
|
||||||
|
*out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';');
|
||||||
|
break;
|
||||||
|
case Ch('&'):
|
||||||
|
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*out++ = *begin; // No expansion, copy character
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++begin; // Step to next character
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill given output iterator with repetitions of the same character
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt fill_chars(OutIt out, int n, Ch ch)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; ++i)
|
||||||
|
*out++ = ch;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find character
|
||||||
|
template<class Ch, Ch ch>
|
||||||
|
inline bool find_char(const Ch *begin, const Ch *end)
|
||||||
|
{
|
||||||
|
while (begin != end)
|
||||||
|
if (*begin++ == ch)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Internal printing operations
|
||||||
|
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||||
|
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags);
|
||||||
|
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||||
|
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||||
|
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||||
|
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||||
|
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||||
|
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||||
|
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent);
|
||||||
|
|
||||||
|
// Print node
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||||
|
{
|
||||||
|
// Print proper node type
|
||||||
|
switch (node->type())
|
||||||
|
{
|
||||||
|
|
||||||
|
// Document
|
||||||
|
case node_document:
|
||||||
|
out = print_children(out, node, flags, indent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Element
|
||||||
|
case node_element:
|
||||||
|
out = print_element_node(out, node, flags, indent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Data
|
||||||
|
case node_data:
|
||||||
|
out = print_data_node(out, node, flags, indent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// CDATA
|
||||||
|
case node_cdata:
|
||||||
|
out = print_cdata_node(out, node, flags, indent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Declaration
|
||||||
|
case node_declaration:
|
||||||
|
out = print_declaration_node(out, node, flags, indent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Comment
|
||||||
|
case node_comment:
|
||||||
|
out = print_comment_node(out, node, flags, indent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Doctype
|
||||||
|
case node_doctype:
|
||||||
|
out = print_doctype_node(out, node, flags, indent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Pi
|
||||||
|
case node_pi:
|
||||||
|
out = print_pi_node(out, node, flags, indent);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Unknown
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If indenting not disabled, add line break after node
|
||||||
|
if (!(flags & print_no_indenting))
|
||||||
|
*out = Ch('\n'), ++out;
|
||||||
|
|
||||||
|
// Return modified iterator
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print children of the node
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||||
|
{
|
||||||
|
for (xml_node<Ch> *child = node->first_node(); child; child = child->next_sibling())
|
||||||
|
out = print_node(out, child, flags, indent);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print attributes of the node
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags)
|
||||||
|
{
|
||||||
|
for (xml_attribute<Ch> *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute())
|
||||||
|
{
|
||||||
|
if (attribute->name() && attribute->value())
|
||||||
|
{
|
||||||
|
// Print attribute name
|
||||||
|
*out = Ch(' '), ++out;
|
||||||
|
out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out);
|
||||||
|
*out = Ch('='), ++out;
|
||||||
|
// Print attribute value using appropriate quote type
|
||||||
|
if (find_char<Ch, Ch('"')>(attribute->value(), attribute->value() + attribute->value_size()))
|
||||||
|
{
|
||||||
|
*out = Ch('\''), ++out;
|
||||||
|
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out);
|
||||||
|
*out = Ch('\''), ++out;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*out = Ch('"'), ++out;
|
||||||
|
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out);
|
||||||
|
*out = Ch('"'), ++out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print data node
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||||
|
{
|
||||||
|
assert(node->type() == node_data);
|
||||||
|
if (!(flags & print_no_indenting))
|
||||||
|
out = fill_chars(out, indent, Ch('\t'));
|
||||||
|
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print data node
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||||
|
{
|
||||||
|
assert(node->type() == node_cdata);
|
||||||
|
if (!(flags & print_no_indenting))
|
||||||
|
out = fill_chars(out, indent, Ch('\t'));
|
||||||
|
*out = Ch('<'); ++out;
|
||||||
|
*out = Ch('!'); ++out;
|
||||||
|
*out = Ch('['); ++out;
|
||||||
|
*out = Ch('C'); ++out;
|
||||||
|
*out = Ch('D'); ++out;
|
||||||
|
*out = Ch('A'); ++out;
|
||||||
|
*out = Ch('T'); ++out;
|
||||||
|
*out = Ch('A'); ++out;
|
||||||
|
*out = Ch('['); ++out;
|
||||||
|
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
||||||
|
*out = Ch(']'); ++out;
|
||||||
|
*out = Ch(']'); ++out;
|
||||||
|
*out = Ch('>'); ++out;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print element node
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||||
|
{
|
||||||
|
assert(node->type() == node_element);
|
||||||
|
|
||||||
|
// Print element name and attributes, if any
|
||||||
|
if (!(flags & print_no_indenting))
|
||||||
|
out = fill_chars(out, indent, Ch('\t'));
|
||||||
|
*out = Ch('<'), ++out;
|
||||||
|
out = copy_chars(node->name(), node->name() + node->name_size(), out);
|
||||||
|
out = print_attributes(out, node, flags);
|
||||||
|
|
||||||
|
// If node is childless
|
||||||
|
if (node->value_size() == 0 && !node->first_node())
|
||||||
|
{
|
||||||
|
// Print childless node tag ending
|
||||||
|
*out = Ch('/'), ++out;
|
||||||
|
*out = Ch('>'), ++out;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Print normal node tag ending
|
||||||
|
*out = Ch('>'), ++out;
|
||||||
|
|
||||||
|
// Test if node contains a single data node only (and no other nodes)
|
||||||
|
xml_node<Ch> *child = node->first_node();
|
||||||
|
if (!child)
|
||||||
|
{
|
||||||
|
// If node has no children, only print its value without indenting
|
||||||
|
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
|
||||||
|
}
|
||||||
|
else if (child->next_sibling() == 0 && child->type() == node_data)
|
||||||
|
{
|
||||||
|
// If node has a sole data child, only print its value without indenting
|
||||||
|
out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Print all children with full indenting
|
||||||
|
if (!(flags & print_no_indenting))
|
||||||
|
*out = Ch('\n'), ++out;
|
||||||
|
out = print_children(out, node, flags, indent + 1);
|
||||||
|
if (!(flags & print_no_indenting))
|
||||||
|
out = fill_chars(out, indent, Ch('\t'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print node end
|
||||||
|
*out = Ch('<'), ++out;
|
||||||
|
*out = Ch('/'), ++out;
|
||||||
|
out = copy_chars(node->name(), node->name() + node->name_size(), out);
|
||||||
|
*out = Ch('>'), ++out;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print declaration node
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||||
|
{
|
||||||
|
// Print declaration start
|
||||||
|
if (!(flags & print_no_indenting))
|
||||||
|
out = fill_chars(out, indent, Ch('\t'));
|
||||||
|
*out = Ch('<'), ++out;
|
||||||
|
*out = Ch('?'), ++out;
|
||||||
|
*out = Ch('x'), ++out;
|
||||||
|
*out = Ch('m'), ++out;
|
||||||
|
*out = Ch('l'), ++out;
|
||||||
|
|
||||||
|
// Print attributes
|
||||||
|
out = print_attributes(out, node, flags);
|
||||||
|
|
||||||
|
// Print declaration end
|
||||||
|
*out = Ch('?'), ++out;
|
||||||
|
*out = Ch('>'), ++out;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print comment node
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||||
|
{
|
||||||
|
assert(node->type() == node_comment);
|
||||||
|
if (!(flags & print_no_indenting))
|
||||||
|
out = fill_chars(out, indent, Ch('\t'));
|
||||||
|
*out = Ch('<'), ++out;
|
||||||
|
*out = Ch('!'), ++out;
|
||||||
|
*out = Ch('-'), ++out;
|
||||||
|
*out = Ch('-'), ++out;
|
||||||
|
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
||||||
|
*out = Ch('-'), ++out;
|
||||||
|
*out = Ch('-'), ++out;
|
||||||
|
*out = Ch('>'), ++out;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print doctype node
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||||
|
{
|
||||||
|
assert(node->type() == node_doctype);
|
||||||
|
if (!(flags & print_no_indenting))
|
||||||
|
out = fill_chars(out, indent, Ch('\t'));
|
||||||
|
*out = Ch('<'), ++out;
|
||||||
|
*out = Ch('!'), ++out;
|
||||||
|
*out = Ch('D'), ++out;
|
||||||
|
*out = Ch('O'), ++out;
|
||||||
|
*out = Ch('C'), ++out;
|
||||||
|
*out = Ch('T'), ++out;
|
||||||
|
*out = Ch('Y'), ++out;
|
||||||
|
*out = Ch('P'), ++out;
|
||||||
|
*out = Ch('E'), ++out;
|
||||||
|
*out = Ch(' '), ++out;
|
||||||
|
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
||||||
|
*out = Ch('>'), ++out;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print pi node
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
||||||
|
{
|
||||||
|
assert(node->type() == node_pi);
|
||||||
|
if (!(flags & print_no_indenting))
|
||||||
|
out = fill_chars(out, indent, Ch('\t'));
|
||||||
|
*out = Ch('<'), ++out;
|
||||||
|
*out = Ch('?'), ++out;
|
||||||
|
out = copy_chars(node->name(), node->name() + node->name_size(), out);
|
||||||
|
*out = Ch(' '), ++out;
|
||||||
|
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
||||||
|
*out = Ch('?'), ++out;
|
||||||
|
*out = Ch('>'), ++out;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//! \endcond
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Printing
|
||||||
|
|
||||||
|
//! Prints XML to given output iterator.
|
||||||
|
//! \param out Output iterator to print to.
|
||||||
|
//! \param node Node to be printed. Pass xml_document to print entire document.
|
||||||
|
//! \param flags Flags controlling how XML is printed.
|
||||||
|
//! \return Output iterator pointing to position immediately after last character of printed text.
|
||||||
|
template<class OutIt, class Ch>
|
||||||
|
inline OutIt print(OutIt out, const xml_node<Ch> &node, int flags = 0)
|
||||||
|
{
|
||||||
|
return internal::print_node(out, &node, flags, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef RAPIDXML_NO_STREAMS
|
||||||
|
|
||||||
|
//! Prints XML to given output stream.
|
||||||
|
//! \param out Output stream to print to.
|
||||||
|
//! \param node Node to be printed. Pass xml_document to print entire document.
|
||||||
|
//! \param flags Flags controlling how XML is printed.
|
||||||
|
//! \return Output stream.
|
||||||
|
template<class Ch>
|
||||||
|
inline std::basic_ostream<Ch> &print(std::basic_ostream<Ch> &out, const xml_node<Ch> &node, int flags = 0)
|
||||||
|
{
|
||||||
|
print(std::ostream_iterator<Ch>(out), node, flags);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.
|
||||||
|
//! \param out Output stream to print to.
|
||||||
|
//! \param node Node to be printed.
|
||||||
|
//! \return Output stream.
|
||||||
|
template<class Ch>
|
||||||
|
inline std::basic_ostream<Ch> &operator <<(std::basic_ostream<Ch> &out, const xml_node<Ch> &node)
|
||||||
|
{
|
||||||
|
return print(out, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,122 @@
|
||||||
|
#ifndef RAPIDXML_UTILS_HPP_INCLUDED
|
||||||
|
#define RAPIDXML_UTILS_HPP_INCLUDED
|
||||||
|
|
||||||
|
// Copyright (C) 2006, 2009 Marcin Kalicinski
|
||||||
|
// Version 1.13
|
||||||
|
// Revision $DateTime: 2009/05/13 01:46:17 $
|
||||||
|
//! \file rapidxml_utils.hpp This file contains high-level rapidxml utilities that can be useful
|
||||||
|
//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective.
|
||||||
|
|
||||||
|
#include "rapidxml.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace rapidxml
|
||||||
|
{
|
||||||
|
|
||||||
|
//! Represents data loaded from a file
|
||||||
|
template<class Ch = char>
|
||||||
|
class file
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Loads file into the memory. Data will be automatically destroyed by the destructor.
|
||||||
|
//! \param filename Filename to load.
|
||||||
|
file(const char *filename)
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// Open stream
|
||||||
|
basic_ifstream<Ch> stream(filename, ios::binary);
|
||||||
|
if (!stream)
|
||||||
|
throw runtime_error(string("cannot open file ") + filename);
|
||||||
|
stream.unsetf(ios::skipws);
|
||||||
|
|
||||||
|
// Determine stream size
|
||||||
|
stream.seekg(0, ios::end);
|
||||||
|
size_t size = stream.tellg();
|
||||||
|
stream.seekg(0);
|
||||||
|
|
||||||
|
// Load data and add terminating 0
|
||||||
|
m_data.resize(size + 1);
|
||||||
|
stream.read(&m_data.front(), static_cast<streamsize>(size));
|
||||||
|
m_data[size] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Loads file into the memory. Data will be automatically destroyed by the destructor
|
||||||
|
//! \param stream Stream to load from
|
||||||
|
file(std::basic_istream<Ch> &stream)
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// Load data and add terminating 0
|
||||||
|
stream.unsetf(ios::skipws);
|
||||||
|
m_data.assign(istreambuf_iterator<Ch>(stream), istreambuf_iterator<Ch>());
|
||||||
|
if (stream.fail() || stream.bad())
|
||||||
|
throw runtime_error("error reading stream");
|
||||||
|
m_data.push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets file data.
|
||||||
|
//! \return Pointer to data of file.
|
||||||
|
Ch *data()
|
||||||
|
{
|
||||||
|
return &m_data.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets file data.
|
||||||
|
//! \return Pointer to data of file.
|
||||||
|
const Ch *data() const
|
||||||
|
{
|
||||||
|
return &m_data.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Gets file data size.
|
||||||
|
//! \return Size of file data, in characters.
|
||||||
|
std::size_t size() const
|
||||||
|
{
|
||||||
|
return m_data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<Ch> m_data; // File data
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Counts children of node. Time complexity is O(n).
|
||||||
|
//! \return Number of children of node
|
||||||
|
template<class Ch>
|
||||||
|
inline std::size_t count_children(xml_node<Ch> *node)
|
||||||
|
{
|
||||||
|
xml_node<Ch> *child = node->first_node();
|
||||||
|
std::size_t count = 0;
|
||||||
|
while (child)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
child = child->next_sibling();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Counts attributes of node. Time complexity is O(n).
|
||||||
|
//! \return Number of attributes of node
|
||||||
|
template<class Ch>
|
||||||
|
inline std::size_t count_attributes(xml_node<Ch> *node)
|
||||||
|
{
|
||||||
|
xml_attribute<Ch> *attr = node->first_attribute();
|
||||||
|
std::size_t count = 0;
|
||||||
|
while (attr)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
attr = attr->next_attribute();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue