Art C Art C - 3 months ago 23
C++ Question

C++ parsing sub nodes of xml

Well here I am again working on this open source game, anyway I made this parser for an xml file however I am unsure how to parse the vocation's children nodes, here is the code and the xml for the function, I want to be able to loop through its children, I assuming I need to create another for loop, however I am not quite sure how I should would reference vocation's child nodes, I am not concerned with their attributes atm.

bool Game::loadCreatureAdditionalAttributes()
{
pugi::xml_document data;
pugi::xml_parse_result result = data.load_file("data/XML/attributes.xml");
if (!result) {
printXMLError("Error - Game::loadCreatureAdditionalAttributes", "data/XML/attributes.xml", result);
return false;
}
bool attributesEnabled = false;
for (auto attributeNode : data.child("attributes").children()) {
if (strcasecmp(attributeNode.name(), "additionalAttributes") == 0) {
if (attributeNode.attribute("enabled").as_bool()) {
if (strcasecmp(attributeNode.name(), "vocation") == 0) {
pugi::xml_attribute vocation = attributeNode.attribute("id");
uint32_t aNode;
pugi::xml_attribute attr = attributeNode.attribute(attributeNode.name());
if (attr) {
aNode = pugi::cast<uint32_t>(attr.value());
}
else {
aNode = 0;
}
CreatureAttributes[pugi::cast<uint32_t>(vocation.value())][attributeNode.name()] = aNode;
}
}
}
}
return true;
}


And now the xml

<?xml version="1.0" encoding="UTF-8"?>
<attributes>
<additionalAttributes enabled = "0" />
<vocation id = "1">
<attribute stamina = "1" />
<attribute strength = "1" />
<attribute intelligence = "1" />
<attribute dexterity = "1" />
<attribute wisdom = "1" />
<attribute luck = "1" />
<attribute critical = "1" />
<attribute block = "1" />
<attribute experienceGain = "1" />
<attribute power = "1" />
<attribute precision = "1" />
</vocation>
<vocation id = "2">
<attribute stamina = "1" />
<attribute strength = "1" />
<attribute intelligence = "1" />
<attribute dexterity = "1" />
<attribute wisdom = "1" />
<attribute luck = "1" />
<attribute critical = "1" />
<attribute block = "1" />
<attribute experienceGain = "1" />
<attribute power = "1" />
<attribute precision = "1" />
</vocation>
</attributes>

Answer

I like pugixml a lot it makes parsing very easy. However your XML format is kind of tricky, I would consider storing your <attributes> differently.

Currently you can iterate through the vocations and the attributes like this:

#include <iostream>

#define PUGIXML_HEADER_ONLY
#include "pugixml.hpp"

auto xml = R"(
<?xml version="1.0" encoding="UTF-8"?>
<attributes>
    <additionalAttributes enabled = "0" />
    <vocation id = "1">
        <attribute stamina = "1" />
        <attribute strength = "1" />
        <attribute intelligence = "1" />
    </vocation>
    <vocation id = "2">
        <attribute stamina = "1" />
        <attribute strength = "1" />
        <attribute intelligence = "1" />
    </vocation>
</attributes>
)";

int main()
{
    pugi::xml_document doc;

    doc.load(xml);

    for(auto vocation: doc.child("attributes").children("vocation"))
    {
        std::cout << "vocation id:" << vocation.attribute("id").as_string() << '\n';

        for(auto attribute: vocation.children("attribute"))
        {
            for(auto& a: attribute.attributes())
            {
                std::cout << "    attribute: " << a.name() << '\n';
                std::cout << "             : " << a.value() << '\n';
            }
        }
    }
}

I would probably organize my XML more like this:

#include <iostream>

#define PUGIXML_HEADER_ONLY
#include "pugixml.hpp"

auto xml = R"(
<?xml version="1.0" encoding="UTF-8"?>
<attributes>
    <additionalAttributes enabled = "0" />
    <vocation id = "1">
        <stamina>1</stamina>
        <strength>1</strength>
        <intelligence>1</intelligence>
    </vocation>
    <vocation id = "2">
        <stamina>1</stamina>
        <strength>1</strength>
        <intelligence>1</intelligence>
    </vocation>
</attributes>
)";

int main()
{
    pugi::xml_document doc;

    doc.load(xml);

    for(auto vocation: doc.child("attributes").children("vocation"))
    {
        std::cout << "vocation id:" << vocation.attribute("id").as_string() << '\n';
        std::cout << "           : stamina = " << vocation.child("stamina").text().as_string() << '\n';
        std::cout << "           : strength = " << vocation.child("strength").text().as_string() << '\n';
        std::cout << "           : intelligence = " << vocation.child("intelligence").text().as_string() << '\n';
        std::cout << '\n';

    }
}
Comments