serialization: Add FileSerializer and XmlSerializer

This commit is contained in:
Joshua Scott 2019-01-07 22:49:12 +00:00
parent 4833f7fb76
commit 69189e0fba
25 changed files with 2013 additions and 121 deletions

View File

@ -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>();
} }
}; };
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -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>();
} }
}; };

View File

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

View File

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

View File

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

View File

@ -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);
};
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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());
}
}
}
}

View File

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

View File

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

View File

@ -1,4 +1,4 @@
{ JSON{
"_pclass_meta": { "_pclass_meta": {
"type_hash": 2069042008 "type_hash": 2069042008
}, },

View File

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

View File

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

View File

@ -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");
}
} }
} }

448
third_party/rapidxml_print.hpp vendored Normal file
View File

@ -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 (&lt; &gt; &apos; &quot; &amp;)
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

122
third_party/rapidxml_utils.hpp vendored Normal file
View File

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