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

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

我有一个对象列表,并想将其序列化为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 ++,我不得不这样做来序列化我的对象。这是我的老问题:How to change or delete tags in boost serialization?和我的对象标题如下:

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对象:saver(_doc.root(),“PingLog”,pingLog);

打印将是这样的:

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

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

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

我怎么能解决这个问题,我的错是什么?

c++ xml for-loop serialization pugixml
2个回答
0
投票

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

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

我的新Xml Saver是这样的:

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中我们有一个不必要的标签“”这就是为什么我不得不改变我的服务器端代码。

无论如何,谢谢你的意见和支持。


0
投票

如果你能够在结构中添加一些小修改,并且你有C ++ 11.你可以尝试我的lib,可以在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>
© www.soinside.com 2019 - 2024. All rights reserved.