2
<Node>
  <A>
    <B id = "it_DEN"></B>
  </A>
  <A>
    <B id = "en_KEN"></B>
  </A>
  <A>
    <B id = "it_BEN"></B>
  </A>
</Node>

How can I remove child node of <A></A> that has child node <B></B> which has attribute id not starts with it using PugiXML. The result would be as below:

<Node>
  <A>
    <B id = "it_DEN"></B>
  </A>
  <A>
    <B id = "it_BEN"></B>
  </A>
</Node>
vincent911001
  • 523
  • 1
  • 6
  • 20
  • 1
    So what do you think? What method did you come up with? – Chanhee Jo May 25 '16 at 04:12
  • I am trying to use Xpath to search for child nodes that i dont want and then remove it from the parent node but it seems, the API do not have that kind of functionality. So, i guess, i would try to remove it all and then add the required child nodes back if there is no other alternative. – vincent911001 May 25 '16 at 04:51

2 Answers2

7

This is slightly tricky if you want to remove nodes while iterating (to keep the code single-pass). Here's one way to do it:

bool should_remove(pugi::xml_node node)
{
    const char* id = node.child("B").attribute("id").value();
    return strncmp(id, "it_", 3) != 0;
}

for (pugi::xml_node child = doc.child("Node").first_child(); child; )
{
    pugi::xml_node next = child.next_sibling();

    if (should_remove(child))
        child.parent().remove_child(child);

    child = next;
}

Alternatively you can just use XPath and remove the results:

pugi::xpath_node_set ns = doc.select_nodes("/Node/A[B[not(starts-with(@id, 'it_'))]]");

for (auto& n: ns)
    n.node().parent().remove_child(n.node());
zeuxcg
  • 9,216
  • 1
  • 26
  • 33
0

Another way is to increment the iterator before removing the child. To remove an attribute while iterating.

for(pugi::xml_attribute_iterator it = node.attributes_begin(); it != node.attributes_end();){
    pugi::xml_attribute attr = *it++;
    if(should_remove(attr)){
        node.remove_attribute(attr);
    }
}
Neel Basu
  • 12,638
  • 12
  • 82
  • 146