Proper way to parse XML using pugixml

Chimera picture Chimera · Apr 22, 2013 · Viewed 15.5k times · Source

I have an XML file that I need to parse and I'm struggling with using PugiXML to parse out all elements and attributes.

I have the following XML file:

    <?xml version = '1.0' encoding = 'UTF-8'?>
    <PanelList DefaultTheme="konami" >
            <Panel xpos="0" ypos="0" width="800" height="600" imagepath="./%THEME%/background_uga_large.png" name="AttractMode0" >
                <GameViewport xpos="0" ypos="0" width="630" height="526" name="gameviewport"/>
                <ValueLabel xpos="120" ypos="550" width="448" height="30" fontsize="18" color="white" text="Please, insert your card" name="StatusAttract" errorimagepath="./%THEME%/panel_top_error.png" pageattpendingpath="./%THEME%/panel_top_att.png" drinkpendingpath="./%THEME%/panel_top_drink.png" onclick="ShowPanel(special_panel999)" />
                <StreamingImage xpos="637" ypos="3" width="160" height="120" volume="0" text="udp:@:9001" name="attract1" />
                <Image xpos="637" ypos="130" width="160" height="390" imagepath="http://n2web/images/attract-uga.gif" />
                <FunctionButton xpos="6" ypos="534" width="68" height="60" imagepath="./%THEME%/button_shrink.png" clickimagepath="./%THEME%/button_shrink_down.png" name="window" onclick="ShowPanel(AttractModeSmall)"/>
                <FunctionButton xpos="650" ypos="534" width="68" height="60" imagepath="./%THEME%/button_attendantstart_up.png" clickimagepath="./%THEME%/button_attendantstart_down.png" name="pageattstart" onclick="PageAttendantRequest" enable="MW_ONLINE == TRUE"/>
                <FunctionButton xpos="725" ypos="534" width="68" height="60" imagepath="./%THEME%/button_drinkstart_up.png" clickimagepath="./%THEME%/button_drinkstart_down.png" name="drinkstart" onclick="DrinkRequest" enable="MW_ONLINE == TRUE"/>
            </Panel>

            <Panel xpos="0" ypos="0" width="800" height="600" imagepath="./%THEME%/background_uga_large.png" name="AttractMode1" >
                <GameViewport xpos="0" ypos="0" width="630" height="526" name="gameviewport"/>
                <ValueLabel xpos="120" ypos="550" width="448" height="30" fontsize="18" color="white" text="Please, insert your card" name="StatusAttract" errorimagepath="./%THEME%/panel_top_error.png" pageattpendingpath="./%THEME%/panel_top_att.png" drinkpendingpath="./%THEME%/panel_top_drink.png" onclick="ShowPanel(special_panel999)" />
                <StreamingImage xpos="637" ypos="3" width="160" height="120" volume="0" text="udp:@:9001" name="attract1" />
                <Image xpos="637" ypos="130" width="160" height="390" imagepath="http://n2web/images/attract-uga.gif" />
                <FunctionButton xpos="6" ypos="534" width="68" height="60" imagepath="./%THEME%/button_shrink.png" clickimagepath="./%THEME%/button_shrink_down.png" name="window" onclick="ShowPanel(AttractModeSmall)"/>
                <FunctionButton xpos="650" ypos="534" width="68" height="60" imagepath="./%THEME%/button_attendantstart_up.png" clickimagepath="./%THEME%/button_attendantstart_down.png" name="pageattstart" onclick="PageAttendantRequest" enable="MW_ONLINE == TRUE"/>
                <FunctionButton xpos="725" ypos="534" width="68" height="60" imagepath="./%THEME%/button_drinkstart_up.png" clickimagepath="./%THEME%/button_drinkstart_down.png" name="drinkstart" onclick="DrinkRequest" enable="MW_ONLINE == TRUE"/>
            </Panel>    
</PanelList>

With the following code I can get the attributes of each of the two "Panel" objects.

#include "pugixml.hpp"
#include <iostream>

int main()
{
    pugi::xml_document doc;
    if (!doc.load_file("allpanels.xml")) return -1;


    pugi::xml_node panels = doc.child("PanelList");

    std::cout << panels.name() << std::endl;

    for (pugi::xml_node panel = panels.first_child(); panel; panel = panel.next_sibling())
    {
        std::cout << panel.name() << std::endl;

        for (pugi::xml_attribute attr = panel.first_attribute(); attr; attr = attr.next_attribute())
        {
            std::cout << " " << attr.name() << "=" << attr.value() << std::endl;
        }
        std::cout << std::endl;
    }
    std::cout << std::endl;
}

The output is as follows:

jrnVM gtkSign $ ./gtkSign 
PanelList
Panel
 xpos=0
 ypos=0
 width=800
 height=600
 imagepath=./%THEME%/background_uga_large.png
 name=AttractMode0

Panel
 xpos=0
 ypos=0
 width=800
 height=600
 imagepath=./%THEME%/background_uga_large.png
 name=AttractMode1

I can see that I'm getting the attributes for each of the two "Panel" objects, but not the elements for each. Each "Panel" object in the file will have different elements so I can't traverse the tree looking for particular elements or attributes.

The question is: How do I modify the code so that I can iterate though all of the "Panel" objects and get all of the information for each one?

Any help would be greatly appreciated.

EDIT:

With the help of "john" I was able to come up with this solution. Thanks John.

#include "pugixml.hpp"
#include <iostream>
#include <sstream>

int main()
{
    pugi::xml_document doc;
    std::string namePanel;

    if (!doc.load_file("allpanels.xml")) return -1;


    pugi::xml_node panels = doc.child("PanelList");

    std::cout << panels.name() << std::endl;

    for (pugi::xml_node panel = panels.first_child(); panel; panel = panel.next_sibling())
    {
        //We found a "Panel" -- print it's attributes
        for (pugi::xml_attribute attr = panel.first_attribute(); attr; attr = attr.next_attribute())
        {
            std::cout << " " << attr.name() << "=" << attr.value() << std::endl;

            std::string attrName = attr.name();
            if( !attrName.compare("name") )
            {
                namePanel = attr.value();
            }
        }
        std::cout << std::endl;

        std::cout << "Panel: "  << namePanel << std::endl;
        //Now print all elements and attributes of current "Panel"
        for (pugi::xml_node child = panel.first_child(); child; child = child.next_sibling())
        {
            std::cout << child.name() << std::endl;     // get element name
            // iterate through all attributes
            for (pugi::xml_attribute attr = child.first_attribute(); attr; attr = attr.next_attribute())
            {
                std::cout << " " << attr.name() << "=" << attr.value() << std::endl;
            }
            std::cout << std::endl;
        }
    }
    std::cout << std::endl;
}

EDIT: The above works, but I can't help but think there is a better way that involves a single loop.

Answer