我正在使用 boost ( 1.69.0 ) 序列化来生成 XML 模板文件。 不幸的是我想要达到的结果很糟糕。 这是示例代码:
struct A
{
struct B
{
int bInt;
bool bBool;
private:
friend class boost::serialization::access;
template < class Archive >
void serialize( Archive& ar, const unsigned int version )
{
ar& BOOST_SERIALIZATION_NVP( bInt );
ar& BOOST_SERIALIZATION_NVP( bBool );
}
};
std::vector< B > aVector;
int aInt;
bool abool;
private:
friend class boost::serialization::access;
template < class Archive >
void serialize( Archive& ar, const unsigned int version )
{
ar& BOOST_SERIALIZATION_NVP( aVector );
ar& BOOST_SERIALIZATION_NVP( aInt );
ar& BOOST_SERIALIZATION_NVP( abool );
}
};
并打电话
void createXml( const std::filesystem::path& filePath )
{
if (filePath.empty())
{
return;
}
A data;
wofstream wofFile( filePath.c_str() );
boost::archive::xml_woarchive warOutArchive( wofFile );
warOutArchive << boost::serialization::make_nvp( "_Template", data );
}
结果:
<boost_serialization signature="serialization::archive" version="17">
<_Template class_id="0" tracking_level="0" version="0">
<aVector class_id="1" tracking_level="0" version="0">
<count>0</count>
<item_version>0</item_version>
</aVector>
<aInt>-858993460</aInt>
<abool>204</abool>
</_Template>
</boost_serialization>
如您所见,模板生成不正确。内容缺失
一切顺利。当我将元素添加到空向量时。
sData.aVector.push_back( { 5, true } );
结果:
<boost_serialization signature="serialization::archive" version="17">
<_Template class_id="0" tracking_level="0" version="0">
<aVector class_id="1" tracking_level="0" version="0">
<count>1</count>
<item_version>0</item_version>
<item class_id="2" tracking_level="0" version="0">
<bint>5</bint>
<bbool>1</bbool>
</item>
</aVector>
<aInt>-858993460</aInt>
<abool>204</abool>
</_Template>
</boost_serialization>
有没有办法在不向容器添加元素的情况下生成xml文件模板?我只需要文档模板/树,不需要值。
您...只想推断文件的 XML 架构吗? Boost Serialization 没有这个。不过,您可以预先填充所有成员,然后使用现有工具从示例输出中推断架构:
要自动预填充任何容器元素,您可以编写一个辅助函数,例如:
namespace detail {
/// simplistic trait to detect if a type is a container
template <typename, typename Enable = void> struct is_container : std::false_type {};
template <typename T>
struct is_container<T,
decltype( //
decltype(std::declval<T>().begin()){}, //
decltype(std::declval<T>().end()){}, //
decltype(std::declval<T>().size()){}, //
void())> : std::true_type {};
// trivial archive implementation that just prepopulates container objects
struct prepopulater {
using is_saving = std::true_type;
using is_loading = std::false_type;
template <class T> void register_type() {}
template <class Deduced> prepopulater& operator<<(Deduced&& v) {
using T = std::decay_t<Deduced>;
if constexpr (is_container<T>{}) {
using V = std::decay_t<decltype(*v.begin())>;
auto& mv = const_cast<T&>(v);
std::cout << "Prepopulate " << boost::core::demangle(typeid(T).name()) << std::endl;
mv.insert(mv.end(), V{});
}
using boost::serialization::serialize;
constexpr auto IL = boost::serialization::implementation_level<T>::value;
if constexpr (IL != boost::serialization::not_serializable &&
IL != boost::serialization::primitive_type) {
auto& mv = const_cast<T&>(v);
serialize(*this, mv, 0);
}
return *this;
}
template <class T> prepopulater& operator&(T&& v) { return *this << std ::forward<T>(v); }
template <class T> prepopulater& operator<<(boost::serialization::nvp<T> t) {
return *this << t.value();
}
template <class T> prepopulater& operator&(boost::serialization::nvp<T> t) {
return *this << t.value();
}
void save_binary(void*, size_t){};
};
} // namespace detail
template <typename Serializable> void prepopulate(Serializable& obj) { detail::prepopulater{} << obj; }
请注意,当您具有递归数据结构时,这会遇到麻烦,因为它会无限加深而耗尽内存。然而,这就是您的问题提出的要求的本质。
例如,将
int bInt
替换为 std::multiset<int> bInts
只是为了更好的演示:
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
// #include <boost/core/demangle.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/set.hpp>
#include <filesystem>
#include <fstream>
#include <iostream>
namespace detail {
/// simplistic trait to detect if a type is a container
template <typename, typename Enable = void> struct is_container : std::false_type {};
template <typename T>
struct is_container<T,
decltype( //
decltype(std::declval<T>().begin()){}, //
decltype(std::declval<T>().end()){}, //
decltype(std::declval<T>().size()){}, //
void())> : std::true_type {};
// trivial archive implementation that just prepopulates container objects
struct prepopulater {
using is_saving = std::true_type;
using is_loading = std::false_type;
template <class T> void register_type() {}
template <class Deduced> prepopulater& operator<<(Deduced&& v) {
using T = std::decay_t<Deduced>;
if constexpr (is_container<T>{}) {
using V = std::decay_t<decltype(*v.begin())>;
auto& mv = const_cast<T&>(v);
// std::cout << "Prepopulate " << boost::core::demangle(typeid(T).name()) << std::endl;
mv.insert(mv.end(), V{});
}
using boost::serialization::serialize;
constexpr auto IL = boost::serialization::implementation_level<T>::value;
if constexpr (IL != boost::serialization::not_serializable &&
IL != boost::serialization::primitive_type) {
auto& mv = const_cast<T&>(v);
serialize(*this, mv, 0);
}
return *this;
}
template <class T> prepopulater& operator&(T&& v) { return *this << std ::forward<T>(v); }
template <class T> prepopulater& operator<<(boost::serialization::nvp<T> t) {
return *this << t.value();
}
template <class T> prepopulater& operator&(boost::serialization::nvp<T> t) {
return *this << t.value();
}
void save_binary(void*, size_t){};
};
} // namespace detail
template <typename Serializable> void prepopulate(Serializable& obj) { detail::prepopulater{} << obj; }
struct A {
struct B {
std::multiset<int> bInts;
bool bBool;
template <class Ar> void serialize(Ar& ar, unsigned) { ar& BOOST_NVP(bInts) & BOOST_NVP(bBool); }
};
std::vector<B> aVector;
int aInt;
bool abool;
template <class Ar> void serialize(Ar& ar, unsigned) { ar& BOOST_NVP(aVector) & BOOST_NVP(aInt) & BOOST_NVP(abool); }
};
void createTemplateXml(std::filesystem::path const& filePath) {
A data;
prepopulate(data);
std::ofstream ofFile(filePath);
boost::archive::xml_oarchive(ofFile) << boost::serialization::make_nvp("_Template", data);
}
int main() {
createTemplateXml("prepopulated.xml");
}
哪写的
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="20">
<_Template class_id="0" tracking_level="0" version="0">
<aVector class_id="1" tracking_level="0" version="0">
<count>1</count>
<item_version>0</item_version>
<item class_id="2" tracking_level="0" version="0">
<bInts>
<count>1</count>
<item_version>0</item_version>
<item>0</item>
</bInts>
<bBool>0</bBool>
</item>
</aVector>
<aInt>0</aInt>
<abool>0</abool>
</_Template>
</boost_serialization>
一个脑死亡的 XSD 可能看起来像这样
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="_Template">
<xs:complexType>
<xs:sequence>
<xs:element name="aVector">
<xs:complexType>
<xs:sequence>
<xs:element name="count" type="xs:integer"/>
<xs:element name="item_version" type="xs:integer"/>
<xs:element name="item">
<xs:complexType>
<xs:sequence>
<xs:element name="bInts">
<xs:complexType>
<xs:sequence>
<xs:element name="count" type="xs:integer"/>
<xs:element name="item_version" type="xs:integer"/>
<xs:element name="item" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="bBool" type="xs:boolean"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="aInt" type="xs:integer"/>
<xs:element name="abool" type="xs:boolean"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>