使用空容器增强序列化

问题描述 投票:0回答:1

我正在使用 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文件模板?我只需要文档模板/树,不需要值。

c++ xml boost xml-serialization boost-serialization
1个回答
0
投票

您...只想推断文件的 XML 架构吗? Boost Serialization 没有这个。不过,您可以预先填充所有成员,然后使用现有工具从示例输出中推断架构:

有任何工具可以从 XML 实例文档生成 XSD 架构吗?

要自动预填充任何容器元素,您可以编写一个辅助函数,例如:

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
只是为了更好的演示:

住在Coliru

#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>
© www.soinside.com 2019 - 2024. All rights reserved.