Search code examples
c++xmlfor-loopserializationpugixml

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


I have an object list and wanna serialize it to xml. My object is:

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

And here i make my list just like this:

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

And i send this list to a method which will serialize my object:

RequestHandler().SendPingServerLog(pingLogList);

And my method is:

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

In this method; i use pugixml library for serialization. Because of non-reflection language c++, i had to do this to serialize my object. Here is my old question: How to change or delete tags in boost serialization? And my object header like this:

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

This struct can serialize object to xml successfully. Just for clear, here my example:

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

if i serialize this pingLog object: saver(_doc.root(), "PingLog", pingLog);

print will be like this:

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

My question is, when i serialize an array i got xml tags for each object. Here an example 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>

How can i fix this, what is my fault?


Solution

  • if anyone would have a problem like this; here is my solution:

    i have just added another struct thanks to @Scheff who is my mentor now.

    My new Xml Saver is like this:

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

    And my new struct is:

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

    I'm using this struct like this:

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

    The only problem is in the xml we have an unnecessary tag "" thats why i had to change my server side code too.

    Anyway thank you for your comments and support.