5

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.

Chimera
  • 5,884
  • 7
  • 49
  • 81
  • Do you have to use C++? XSLT is usually the language of choice for parsing XML documents. Look at xsltproc, for example. – Dave Jarvis Apr 22 '13 at 20:25
  • @DaveJarvis Unfortunately, yes I must use C++ as this is just going to be a part of a larger C++ application. This is my first attempt at parsing XML in C++ and I'm also fairly new to XML so much of the language regarding XML is greek to me still. – Chimera Apr 22 '13 at 20:29
  • 1
    @Chimera: In that case, use an XPath expression to extract the elements you need; avoid writing code to iterate through the document to find the elements you require. XPath expressions were created specifically for extracting XML content. http://pugixml.googlecode.com/svn/tags/release-0.9/docs/manual/xpath.html – Dave Jarvis Apr 22 '13 at 20:41
  • @DaveJarvis The only issue with using XPath, if I understand correctly, is that each "Panel" won't always have each element type and each element may not always have the same attributes. So I can't rely X number of elements each with Y attributes while parsing. – Chimera Apr 22 '13 at 20:45
  • @john Yes, I thought so as well, but I guess I just don't know how to find all of the elements and attributes of each "Panel". Basically I don't grok how to set the xml_node.first_child() to iterate through elements and attributes of each Panel... – Chimera Apr 22 '13 at 20:47
  • 1
    It will may helps you : http://www.dreamincode.net/forums/topic/244725-how-to-parse-xml-in-c/ – gtzinos May 26 '15 at 19:48
  • @Chimera You are welcome my friend !!! – gtzinos May 27 '15 at 20:19

0 Answers0