C ++使用for循环将对象列表序列化为XML

C++ serialize an object list to XML with for loop

我有一个对象列表,想将其序列化为 xml。我的对象是:

struct PingLogDto
{
    int PingLogID;
    int HardwareHostID;
    std::string PingLogRoundtripTime;
};

在这里我列出了这样的清单:

vector<PingLogDto> pingLogList;

for(int i = 0; i < 3; i++)
{
    PingLogDto pingLog;
    pingLog.HardwareHostID = 1;
    pingLog.PingLogRoundtripTime = std::to_string(i);

    pingLogList.push_back(pingLog);
}

然后我将此列表发送到一个将序列化我的对象的方法:

RequestHandler().SendPingServerLog(pingLogList);

而我的方法是:

void RequestHandler::SendPingServerLog(vector<PingLogDto> pingLogList)
{
    std::string xml = "";

    for (auto &pingLog : pingLogList) 
    {
        std::string docStringValue;
        PingLogDto pingLogDto;
        PingLogDtoXml::Saver saver;

        pugi::xml_document _doc;

        saver(_doc.root(), "PingLog", pingLog);

        std::stringstream ss;
        _doc.save(ss);
        docStringValue =  ss.str();
        xml += docStringValue;
    }

    std::cout<<"xml: "<<xml<<std::endl;    
}

在这个方法中;我使用 pugixml 库进行序列化。由于非反射语言 c++,我不得不这样做来序列化我的对象。这是我的老问题: 我的对象头是这样的:

struct PingLogDtoXml {
    struct Saver {
        template <typename T>
        void operator()(pugi::xml_node parent, std::string const& name, T const& value) const {
            auto node = named_child(parent, name);
            node.text().set(to_xml(value));
        }

        template <typename C>
        void operator()(pugi::xml_node parent, std::string const& name, std::string const& item_name, C const& container) const {
            auto list = named_child(parent, name);

            for (auto& item : container)
                operator()(list, item_name, item);
        }

        void operator()(pugi::xml_node parent, std::string const& name, PingLogDto const& o) const {
            auto dto = named_child(parent, name);
            operator()(dto, "PingLogID", o.PingLogID);
            operator()(dto, "HardwareHostID", o.HardwareHostID);
            operator()(dto, "PingLogRoundtripTime", o.PingLogRoundtripTime);
        }

    private:
        template <typename T> static T const& to_xml(T const& v) { return v; }
        static char const* to_xml(std::string const& v) { return v.c_str(); }

        pugi::xml_node named_child(pugi::xml_node parent, std::string const& name) const {
            auto child = parent.append_child();
            child.set_name(name.c_str());
            return child;
        }
    };

    struct Loader {
        void operator()(pugi::xml_node parent, std::string const& name, std::string& value) const {
            auto node = parent.first_element_by_path(name.c_str());
            value = node.text().as_string();
        }
        void operator()(pugi::xml_node parent, std::string const& name, int& value) const {
            auto node = parent.first_element_by_path(name.c_str());
            value = node.text().as_int();
        }

        template <typename C>
        void operator()(pugi::xml_node parent, std::string const& name, std::string const& item_name, C& container) const {
            auto list = parent.first_element_by_path(name.c_str());

            for (auto& node : list) {
                if (node.type() != pugi::xml_node_type::node_element) {
                    std::cerr << "Warning: unexpected child node type ignored\n";
                    continue;
                }
                if (node.name() != item_name) {
                    std::cerr << "Warning: unexpected child node ignored (" << node.name() << ")\n";
                    continue;
                }

                container.emplace_back();
                operator()(node, container.back());
            }
        }

        void operator()(pugi::xml_node dto, PingLogDto& o) const {
            operator()(dto, "PingLogID", o.PingLogID);
            operator()(dto, "HardwareHostID", o.HardwareHostID);
            operator()(dto, "PingLogRoundtripTime", o.PingLogRoundtripTime);            
        }

        void operator()(pugi::xml_node parent, std::string const& name, PingLogDto& o) const {
            operator()(parent.first_element_by_path(name.c_str()), o);
        }
    };
};

这个结构可以成功地将对象序列化为xml。为了清楚起见,这里是我的例子:

PingLogDto pingLog;
pingLog.HardwareHostID = 1;
pingLog.PingLogRoundtripTime = "45";

如果我序列化此 pingLog 对象: 保护程序(_doc.root(),"PingLog",pingLog);

打印会是这样的:

<?xml version="1.0"?>
<PingLog>
    <PingLogID>0</PingLogID>
    <HardwareHostID>1</HardwareHostID>
    <PingLogRoundtripTime>45</PingLogRoundtripTime>
</PingLog>

我的问题是,当我序列化一个数组时,我得到了每个对象的 xml 标签。这里有一个例子 xml print:

<?xml version="1.0"?>
<PingLog>
    <PingLogID>0</PingLogID>
    <HardwareHostID>1</HardwareHostID>
    <PingLogRoundtripTime>0</PingLogRoundtripTime>
    <PingLogDate>123</PingLogDate>
</PingLog>
<?xml version="1.0"?>
<PingLog>
    <PingLogID>0</PingLogID>
    <HardwareHostID>1</HardwareHostID>
    <PingLogRoundtripTime>1</PingLogRoundtripTime>
</PingLog>
<?xml version="1.0"?>
<PingLog>
    <PingLogID>0</PingLogID>
    <HardwareHostID>1</HardwareHostID>
    <PingLogRoundtripTime>2</PingLogRoundtripTime>
</PingLog>

我该如何解决这个问题,我的错是什么?

如果有人遇到这样的问题;这是我的解决方案:

感谢@Scheff,我刚刚添加了另一个结构,他现在是我的导师。

我的新Xml保护程序是这样的:

struct Saver {
    template <typename T>
    void operator()(pugi::xml_node parent, std::string const& name, T const& value) const {
        auto node = named_child(parent, name);
        node.text().set(to_xml(value));
    }

    void operator()(pugi::xml_node parent, std::string const& name, PingLogDto const& o) const {
        auto dto = named_child(parent, name);
        operator()(dto, "PingLogID", o.PingLogID);
        operator()(dto, "HardwareHostID", o.HardwareHostID);
        operator()(dto, "PingLogRoundtripTime", o.PingLogRoundtripTime);
    }

    template <typename C>
    void operator()(pugi::xml_node parent, std::string const& name, std::string const& item_name, C const& container) const {
        auto list = named_child(parent, name);

        for (auto& item : container)
            operator()(list, item_name, item);
    }

    void operator()(pugi::xml_node parent, std::string const& name, SendServerPingLogDto const& o) const {
        auto dto = named_child(parent, name);
        operator()(dto, "PingLogList", "PingLog", o.PingLogList);
    }
private:        
    template <typename T> static T const& to_xml(T const& v) { return v; }
    static char const* to_xml(std::string const& v) { return v.c_str(); }

    pugi::xml_node named_child(pugi::xml_node parent, std::string const& name) const {
        auto child = parent.append_child();
        child.set_name(name.c_str());
        return child;
    }
};

我的新结构是:

struct SendServerPingLogDto {
std::vector<PingLogDto> PingLogList;
};

我是这样使用这个结构的:

SendServerPingLogDto sendServerPingLog;

for(int i = 0; i < 10; i++)
{
    PingLogDto pingLog;

    namespace pt = boost::posix_time;
    pt::ptime now = pt::second_clock::local_time();
    std::stringstream ss;
    ss << static_cast<int>(now.date().month()) << "/" << now.date().day() << "/" << now.date().year();

    pingLog.HardwareHostID = 1;
    pingLog.PingLogDate = ss.str();
    pingLog.PingLogID = i + 1;
    pingLog.PingLogRoundtripTime = std::to_string(i);

    sendServerPingLog.PingLogList.push_back(pingLog);
}

    pugi::xml_document _doc;
    Xml::Saver saver;

    saver(_doc.root(), "SendServerPingLog", sendServerPingLog);

    _doc.save_file("SendServerPingLog.xml");

唯一的问题是 xml 我们有一个不必要的标签“”,这就是为什么我也必须更改服务器端代码的原因。

无论如何感谢您的评论和支持。

如果你能在结构中添加一些小的修改,并且你有 C++ 11。你可以试试我的库,可以在 https://github.com/incoder1/IO:

找到

你的例子:

#include <iostream>
#include <files.hpp>
#include <stream.hpp>
#include <xml_types.hpp>

static const char* PROLOGUE = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>";

// optional used to open an xml file, can be cached to std::fostream
static io::s_write_channel create_file_channel(const char* path) {
    io::file f( path );
    std::error_code ec;
    io::s_write_channel ret = f.open_for_write(ec, io::write_open_mode::overwrite);
    io::check_error_code(ec);
    return ret;
}


struct PingLogDto
{
    int PingLogID;
    int HardwareHostID;
    std::string PingLogRoundtripTime; // you can use std::chrono instead

    // A meta-programming type to serialize type into XML
    typedef io::xml::complex_type<PingLogDto,
    std::tuple<>, // this one is for XML attributes
    std::tuple<io::xml::int_element,io::xml::int_element,
            io::xml::string_element> > xml_type;

    // need to be a method to be used in list type
    // and needs to be logically const
    xml_type to_xml_type() const {
        io::xml::int_element idEl("PingLogID", PingLogID);
        io::xml::int_element hostIdEl("HardwareHostID", HardwareHostID);
        io::xml::string_element pingLogRoundtripEl("PingLogRoundtripTime",PingLogRoundtripTime);
        return xml_type("PingLog", std::tuple<>(), std::make_tuple(idEl,hostIdEl,pingLogRoundtripEl));
    }
};


int main()
{
    // open a xml file to write into
    io::channel_ostream<char> xml( create_file_channel("pinlogs.xml"));
    xml << PROLOGUE << std::endl;
    // a meta type for serializing STL container of a specific element
    typedef io::xml::list_type< PingLogDto::xml_type > PinLogsXMLType;
    // a vector of structures to serialize
    std::vector<PingLogDto> logs( {{0,0,"3.1.2018"},{1,1,"4.1.2018"}} );
    // call XML root element as PingLogs
    PinLogsXMLType xt("PingLogs");
    // serialize vector, and name each tag as PingLog
    xt.add_elements( "PingLog", logs.begin(), logs.end() ); 
    // write serialized data into stream and pretty-pint XML
    xt.marshal(xml,1);
    // write same into console
    std::cout << PROLOGUE << std::endl;
    xt.marshal(std::cout,1);
    return 0;
}

结果:

<?xml version="1.0" encoding="UTF-8" ?>
<PingLogs>
    <PingLog>
        <PingLogID>0</PingLogID>
        <HardwareHostID>0</HardwareHostID>
        <PingLogRoundtripTime>3.1.2018</PingLogRoundtripTime>
    </PingLog>
    <PingLog>
        <PingLogID>1</PingLogID>
        <HardwareHostID>1</HardwareHostID>
        <PingLogRoundtripTime>4.1.2018</PingLogRoundtripTime>
    </PingLog>
</PingLogs>