8

Using rapidxml I'm wanting to loop through a set of nodes, and am using what I found to be the best way to do this (from trusty stackoverflow, the doc doesn't appear to have an example of iteration):

while (curNode->next_sibling() !=NULL ) {
    string shiftLength = curNode->first_attribute("shiftLength")->value();
    cout << "Shift Length " << "\t" << shiftLength << endl;
    curNode = curNode->next_sibling();        
}

Unfortunately, on my OSX 10.6 this is leaving out the last sibling node - I guess because in the last iteration of the loop, next_sibling is called twice. I can get at this last node if I write, after the loop:

cout << " LAST IS: " << curNode->first_attribute("shiftLength")->value();

...but that's dodgy, and the program quits at that point.

First question: Could this be a unique foible of my setup (OSX 10.6) or have I coded wrong?

Second question: Does anyone have an example of what they believe is the correct way to iterate through an unknown number of XML nodes using rapidxml?

Thanks guys

Pete

Pete 2233
  • 176
  • 1
  • 1
  • 5
  • 1
    Think of loops in terms of invariants. Yours executes for all nodes with non-NULL next siblings. This is not true for the last node. – Don Reba Jul 16 '11 at 00:44

3 Answers3

12

This is the proper way to iterate though all child nodes of a node in rapidxml:

xml_node<> *node = ...
for (xml_node<> *child = node->first_node(); child; child = child->next_sibling())
{
    // do stuff with child
}
kaalus
  • 3,728
  • 3
  • 24
  • 34
  • This is not convenient if you're about to pick one random node (eg. you need to know number of nodes and pick one at offset X) – Tomáš Zato - Reinstate Monica Dec 07 '14 at 23:44
  • 1
    @Tomas There is always a tradeoff in data structures. To allow fast addition during parsing, rapidxml uses linked lists for child nodes and attributes. Therefore, direct indexing by number is not possible. But you have to remember that rapidxml is not about convenience but performance. – kaalus Dec 08 '14 at 20:59
  • I've written a [function that gives you (at a cost of performance hit) `std::vector` full of child nodes](https://gist.github.com/Darker/4b1ad792ff77a6688388). As soon as the document tree is altered the list is invalid. – Tomáš Zato - Reinstate Monica Dec 08 '14 at 22:48
6

Here's the final code in working form:

xml_node<> *curNode = ... // e. g. parentNode->first_node();
while (curNode) {
    string start = curNode->first_attribute("start")->value();
    string numStaff = curNode->first_attribute("numStaff")->value();
    cout << start << "\t" << numStaff << endl;
    curNode = curNode->next_sibling();
}
stackprotector
  • 4,982
  • 4
  • 12
  • 36
Pete 2233
  • 176
  • 1
  • 1
  • 5
2
while (curNode->next_sibling() !=NULL )

This says "while there's one more node left after the one I'm working on". That's why your loop's stopping early - when curNode is the final sibling, its "next_sibling" will be NULL. This test should work better:

while (curNode !=NULL )
Neil Vass
  • 4,583
  • 2
  • 19
  • 25
  • Thanks Neil (I tried that approach before but get a BAD ACCESS in rapidxml.hpp (line 1031 v 1.13) - aaaaggghh my mistake though I'd tried to look up an attribute after the loop had finished). Thanks again. – Pete 2233 Mar 05 '11 at 11:22