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/util/BitTypes.h"
|
||||
#include "ki/pclass/EnumType.h"
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
namespace ki
|
||||
{
|
||||
|
@ -138,6 +141,11 @@ namespace detail
|
|||
{
|
||||
using type = uint16_t;
|
||||
};
|
||||
template <>
|
||||
struct string_cast_t<char16_t>
|
||||
{
|
||||
using type = int16_t;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enums should be written as 32-bit integers.
|
||||
|
@ -151,12 +159,45 @@ namespace detail
|
|||
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
|
||||
* via std::ostringstream.
|
||||
*/
|
||||
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>
|
||||
{
|
||||
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
|
||||
* primitive type.
|
||||
|
@ -249,7 +374,15 @@ namespace detail
|
|||
template <typename T, typename Enable = void>
|
||||
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, double>();
|
||||
ValueCaster::declare<T, std::string>();
|
||||
ValueCaster::declare<std::string, T>();
|
||||
ValueCaster::declare<T, nlohmann::json>();
|
||||
ValueCaster::declare<nlohmann::json, T>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -312,7 +447,9 @@ namespace detail
|
|||
ValueCaster::declare<T, float>();
|
||||
ValueCaster::declare<T, double>();
|
||||
ValueCaster::declare<T, std::string>();
|
||||
ValueCaster::declare<std::string, T>();
|
||||
ValueCaster::declare<T, nlohmann::json>();
|
||||
ValueCaster::declare<nlohmann::json, T>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -342,7 +479,9 @@ namespace detail
|
|||
ValueCaster::declare<T, uint32_t>();
|
||||
ValueCaster::declare<T, uint64_t>();
|
||||
ValueCaster::declare<T, std::string>();
|
||||
ValueCaster::declare<std::string, T>();
|
||||
ValueCaster::declare<T, nlohmann::json>();
|
||||
ValueCaster::declare<nlohmann::json, T>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -355,8 +494,9 @@ namespace detail
|
|||
{
|
||||
static void declare()
|
||||
{
|
||||
// TODO: Casting string to u16string
|
||||
ValueCaster::declare<std::string, std::u16string>();
|
||||
ValueCaster::declare<std::string, nlohmann::json>();
|
||||
ValueCaster::declare<nlohmann::json, std::string>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -369,8 +509,9 @@ namespace detail
|
|||
{
|
||||
static void declare()
|
||||
{
|
||||
// TODO: Casting u16string to string
|
||||
ValueCaster::declare<std::u16string, std::string>();
|
||||
ValueCaster::declare<std::u16string, nlohmann::json>();
|
||||
ValueCaster::declare<nlohmann::json, std::u16string>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace pclass
|
|||
public:
|
||||
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;
|
||||
|
||||
private:
|
||||
|
|
|
@ -157,10 +157,10 @@ namespace pclass
|
|||
{
|
||||
try
|
||||
{
|
||||
Value deref_value = value.dereference<ValueT>();
|
||||
Value casted_value = value.as<ValueT>();
|
||||
detail::primitive_type_helper<ValueT>::write_to(
|
||||
stream,
|
||||
deref_value.get<ValueT>()
|
||||
casted_value.get<ValueT>()
|
||||
);
|
||||
}
|
||||
catch (runtime_error &e)
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace pclass
|
|||
|
||||
virtual ~IProperty() = default;
|
||||
|
||||
std::string get_name() const;
|
||||
const std::string &get_name() const;
|
||||
hash_t get_name_hash() const;
|
||||
hash_t get_full_hash() const;
|
||||
const Type &get_type() const;
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace pclass
|
|||
{
|
||||
public:
|
||||
explicit TypeSystem(std::unique_ptr<IHashCalculator> &hash_calculator);
|
||||
virtual ~TypeSystem() = default;
|
||||
|
||||
/**
|
||||
* @returns The IHashCalculator instance this TypeSystem uses to calculate
|
||||
|
@ -64,8 +65,6 @@ namespace pclass
|
|||
PrimitiveType<ValueT> &define_primitive(const std::string &name)
|
||||
{
|
||||
detail::caster_declarer<ValueT>::declare();
|
||||
ValueCaster::declare<nlohmann::json, ValueT>();
|
||||
|
||||
auto *type = new PrimitiveType<ValueT>(name, *this);
|
||||
define_type(std::unique_ptr<Type>(
|
||||
dynamic_cast<Type *>(type)
|
||||
|
@ -116,8 +115,6 @@ namespace pclass
|
|||
CppEnumType<EnumT> &define_enum(const std::string &name)
|
||||
{
|
||||
detail::caster_declarer<EnumT>::declare();
|
||||
ValueCaster::declare<nlohmann::json, EnumT>();
|
||||
|
||||
auto *type = new CppEnumType<EnumT>(name, *this);
|
||||
define_type(std::unique_ptr<Type>(
|
||||
dynamic_cast<Type *>(type)
|
||||
|
@ -125,6 +122,12 @@ namespace pclass
|
|||
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.
|
||||
* @tparam ClassT The expected compile-time class.
|
||||
|
@ -133,8 +136,7 @@ namespace pclass
|
|||
template <typename ClassT>
|
||||
std::unique_ptr<ClassT> instantiate(const std::string &name) const
|
||||
{
|
||||
const auto &type = get_type(name);
|
||||
auto object = type.instantiate();
|
||||
auto object = instantiate(name);
|
||||
return std::unique_ptr<ClassT>(
|
||||
dynamic_cast<ClassT *>(object.release())
|
||||
);
|
||||
|
@ -153,8 +155,6 @@ namespace pclass
|
|||
ClassType<ClassT> &define_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
|
||||
// ki::pclass::PropertyClass the base class (if it has been defined)
|
||||
if (base_class == nullptr && has_type("class PropertyClass"))
|
||||
|
|
|
@ -222,6 +222,8 @@ namespace pclass
|
|||
void add_caster()
|
||||
{
|
||||
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>();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,7 +11,8 @@ namespace ki
|
|||
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
|
||||
{
|
||||
|
@ -30,47 +31,50 @@ namespace serialization
|
|||
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
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a new binary serializer.
|
||||
* @param 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 flags Determines how serialized data is formatted.
|
||||
* @param[in] type_system The TypeSystem instance to acquire Type information from.
|
||||
* @param[in] is_file Determines whether or not to write type sizes, and property headers.
|
||||
* @param[in] flags Determines how serialized data is formatted.
|
||||
*/
|
||||
explicit BinarySerializer(const pclass::TypeSystem &type_system,
|
||||
bool is_file, flags flags);
|
||||
virtual ~BinarySerializer() {}
|
||||
virtual ~BinarySerializer() = default;
|
||||
|
||||
/**
|
||||
* @param object The object to write to the stream.
|
||||
* @param stream The stream to write the object to.
|
||||
* Serialize an object into a BitStream.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @param dest Where to load the PropertyClass instance into.
|
||||
* @param stream The stream to read the object from.
|
||||
* @param size The size of the stream's available data.
|
||||
* Deserialize the contents of a BitStream.
|
||||
* @param[out] dest Where to load the PropertyClass instance into.
|
||||
* @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,
|
||||
BitStream &stream, std::size_t size);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @param object The object that is being saved.
|
||||
* @param stream The stream to write the object header to.
|
||||
* @param[in] object The object that is being saved.
|
||||
* @param[in] stream The stream to write the object header to.
|
||||
* @returns Whether or not the object is null.
|
||||
*/
|
||||
virtual bool presave_object(const pclass::PropertyClass *object, BitStream &stream) const;
|
||||
|
||||
/**
|
||||
* Read an object header, and instantiate the necessary PropertyClass.
|
||||
* @param dest Where to instantiate the PropertyClass.
|
||||
* @param stream The stream to read the object header from.
|
||||
* @param[out] dest Where to instantiate the PropertyClass.
|
||||
* @param[in] stream The stream to read the object header from.
|
||||
*/
|
||||
virtual void preload_object(
|
||||
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;
|
||||
|
||||
public:
|
||||
explicit JsonSerializer(pclass::TypeSystem &type_system, bool is_file);
|
||||
virtual ~JsonSerializer() {}
|
||||
explicit JsonSerializer(const pclass::TypeSystem &type_system, bool is_file);
|
||||
virtual ~JsonSerializer() = default;
|
||||
|
||||
std::string save(const pclass::PropertyClass *object) 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:
|
||||
pclass::TypeSystem *m_type_system;
|
||||
const pclass::TypeSystem *m_type_system;
|
||||
bool m_is_file;
|
||||
|
||||
virtual bool presave_object(nlohmann::json &j, const pclass::PropertyClass *object) const;
|
||||
nlohmann::json save_object(const pclass::PropertyClass *object) const;
|
||||
void save_property(nlohmann::json &j, const pclass::IProperty &prop) const;
|
||||
|
||||
virtual bool preload_object(std::unique_ptr<pclass::PropertyClass> &dest, nlohmann::json &j) const;
|
||||
void load_object(std::unique_ptr<pclass::PropertyClass> &dest, nlohmann::json &j) const;
|
||||
void load_property(pclass::IProperty &prop, nlohmann::json &j) const;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
std::string EnumType::Element::get_name() const
|
||||
const std::string &EnumType::Element::get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace pclass
|
|||
return *m_instance;
|
||||
}
|
||||
|
||||
std::string IProperty::get_name() const
|
||||
const std::string &IProperty::get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
|
|
@ -128,6 +128,13 @@ namespace pclass
|
|||
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)
|
||||
{
|
||||
// Does a type with this name already exist?
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
target_sources(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${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/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
|
||||
{
|
||||
JsonSerializer::JsonSerializer(pclass::TypeSystem& type_system,
|
||||
JsonSerializer::JsonSerializer(const pclass::TypeSystem& type_system,
|
||||
const bool is_file)
|
||||
{
|
||||
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": {
|
||||
"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/JsonSerializer.h>
|
||||
#include "ki/pclass/Enum.h"
|
||||
#include "ki/serialization/XmlSerializer.h"
|
||||
#include "ki/serialization/FileSerializer.h"
|
||||
|
||||
using namespace ki;
|
||||
|
||||
|
@ -23,14 +25,18 @@ struct Vector3D
|
|||
friend pclass::detail::value_caster<Vector3D, nlohmann::json>;
|
||||
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(
|
||||
const float x = 0.0f,
|
||||
const float y = 0.0f,
|
||||
const float z = 0.0f)
|
||||
{
|
||||
this->m_x = x;
|
||||
this->m_y = y;
|
||||
this->m_z = z;
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
m_z = z;
|
||||
}
|
||||
|
||||
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(
|
||||
std::unique_ptr<TestObject> &test_object,
|
||||
serialization::BinarySerializer &serializer,
|
||||
serialization::BinarySerializer::flags flags,
|
||||
const std::string &file_suffix)
|
||||
{
|
||||
BitBuffer buffer;
|
||||
|
@ -475,20 +516,18 @@ void test_serializer(
|
|||
// Open the sample data
|
||||
std::ifstream sample(
|
||||
"samples/serialization/" + file_suffix + ".bin",
|
||||
std::ios::binary
|
||||
std::ios::binary | std::ios::ate
|
||||
);
|
||||
REQUIRE(sample.is_open());
|
||||
|
||||
// Load the sample data
|
||||
const auto begin = sample.tellg();
|
||||
sample.seekg(0, std::ios::end);
|
||||
const auto end = sample.tellg();
|
||||
const size_t sample_size = end - begin;
|
||||
const size_t sample_size = sample.tellg();
|
||||
sample.seekg(std::ios::beg);
|
||||
auto *sample_data = new char[sample_size];
|
||||
sample.read(sample_data, sample_size);
|
||||
sample.close();
|
||||
|
||||
serialization::BinarySerializer serializer(*g_type_system, false, flags);
|
||||
SECTION("Saving objects")
|
||||
{
|
||||
// Create a test object, configure it, and write it to our stream
|
||||
|
@ -512,7 +551,6 @@ void test_serializer(
|
|||
// Cleanup
|
||||
delete[] stream_data;
|
||||
}
|
||||
|
||||
SECTION("Loading objects")
|
||||
{
|
||||
// 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,
|
||||
serialization::JsonSerializer &serializer,
|
||||
const std::string &file_suffix)
|
||||
const std::string &filename)
|
||||
{
|
||||
// Open the sample data
|
||||
std::ifstream sample_file(
|
||||
"samples/serialization/" + file_suffix + ".json",
|
||||
std::ios::binary
|
||||
serialization::FileSerializer serializer(*g_type_system);
|
||||
|
||||
// Load an object from a pre-made sample
|
||||
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();
|
||||
const size_t sample_size = end - begin;
|
||||
sample_file.seekg(std::ios::beg);
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void test_file_serializer_save(
|
||||
const std::string &out_filepath,
|
||||
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];
|
||||
sample_file.read(sample_data, sample_size);
|
||||
sample_file.close();
|
||||
sample_ifs.read(sample_data, sample_size);
|
||||
sample_ifs.close();
|
||||
|
||||
// Load the sample data into a string
|
||||
const auto sample = std::string(sample_data, sample_size);
|
||||
// Load output file that was just created
|
||||
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[] 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")
|
||||
{
|
||||
// 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");
|
||||
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
|
||||
// unnecessarily validated by the caller
|
||||
test_object = nullptr;
|
||||
|
||||
// Validate the JSON string
|
||||
REQUIRE(json_string == sample);
|
||||
}
|
||||
|
||||
SECTION("Loading objects")
|
||||
{
|
||||
// Load an object from the sample
|
||||
std::unique_ptr<pclass::PropertyClass> object = nullptr;
|
||||
serializer.load(object, sample);
|
||||
|
||||
// Set test_object so that it is validated by the caller
|
||||
REQUIRE(object != nullptr);
|
||||
test_object = std::unique_ptr<TestObject>(
|
||||
dynamic_cast<TestObject *>(object.release())
|
||||
);
|
||||
REQUIRE(test_object != nullptr);
|
||||
test_file_serializer_load(
|
||||
test_object, filename + ".bin");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -601,53 +674,189 @@ TEST_CASE("Serialization tests", "[serialization]")
|
|||
|
||||
SECTION("BinarySerializer")
|
||||
{
|
||||
SECTION("Regular format without compression")
|
||||
SECTION("Without compression")
|
||||
{
|
||||
serialization::BinarySerializer serializer(
|
||||
*g_type_system, false,
|
||||
serialization::BinarySerializer::flags::NONE
|
||||
test_serializer(
|
||||
test_object,
|
||||
serialization::BinarySerializer::flags::NONE,
|
||||
"regular"
|
||||
);
|
||||
test_serializer(test_object, serializer, "regular");
|
||||
}
|
||||
SECTION("File format without compression")
|
||||
SECTION("With compression")
|
||||
{
|
||||
serialization::BinarySerializer serializer(
|
||||
*g_type_system, true,
|
||||
serialization::BinarySerializer::flags::WRITE_SERIALIZER_FLAGS
|
||||
test_serializer(
|
||||
test_object,
|
||||
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("Regular format")
|
||||
{
|
||||
serialization::JsonSerializer serializer(*g_type_system, false);
|
||||
test_serializer(test_object, serializer, "regular");
|
||||
}
|
||||
// Open the sample data
|
||||
std::ifstream sample_file(
|
||||
"samples/serialization/regular.json",
|
||||
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);
|
||||
test_serializer(test_object, serializer, "file");
|
||||
// Create a test object, configure it, and write it to our stream
|
||||
test_object = g_type_system->instantiate<TestObject>("class TestObject");
|
||||
configure_test_object(*test_object);
|
||||
const auto json_string = serializer.save(test_object.get());
|
||||
|
||||
// Delete the test object here so that it is not
|
||||
// unnecessarily validated by the caller
|
||||
test_object = nullptr;
|
||||
|
||||
// Validate the JSON string
|
||||
REQUIRE(json_string == sample);
|
||||
}
|
||||
SECTION("Loading objects")
|
||||
{
|
||||
// Load an object from the sample
|
||||
std::unique_ptr<pclass::PropertyClass> object = nullptr;
|
||||
serializer.load(object, sample);
|
||||
|
||||
// Set test_object so that it is validated by the caller
|
||||
REQUIRE(object != nullptr);
|
||||
test_object = std::unique_ptr<TestObject>(
|
||||
dynamic_cast<TestObject *>(object.release())
|
||||
);
|
||||
REQUIRE(test_object != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
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