Search code examples
colladapugixml

Find an XML child by an attribute


I'm using PugiXML 1.5 and trying to find a specific child in a giant XML, having a specific attribute. Specifically, this XML is a Collada DAE file.

I know how to find an immediate child ie. by an attribute by doing this:

collada.child("library_visual_scenes").child("visual_scene").
child("node").find_child_by_attribute("instance_geometry", "url", "#myurl");

The problem is that a library_visual_scenes can perfectly have something like this:

<library_visual_scenes>
   <visual_scene>
      <node>
      ...
         <node>
         ..
            <node>
               <instance_geometry url="this_is_what_i_want">
               ...

So, instead of writing the obvious parsing all nodes inside nodes, is there another flavor of find_child_by_attribute that searches into nested nodes for retrieving a child with a specific attribute?

UPDATE

I tried @zeuxcg Xpath suggestion and still didn't fetch me the node. The HTML fragment is the following. I'm not really used to XPath (used to CSS queries mostly)

...
<library_visual_scenes>
  <visual_scene id="xyzscene">
    <node name="EnvironmentAmbientLight">
      <instance_light url="#EnvironmentAmbientLight"/>
    </node>
    <node id="node-foo" name="foo">
      <instance_geometry url="#foo-url">
...

I tried the following Xpath url and didn't worked:

/library_visual_scenes/visual_scene//node/instance_geometry[@url='#foo-url']

But this worked:

//library_visual_scenes/visual_scene//node/instance_geometry[@url='#foo-url']

I learnt that // means search all nodes but library_visual_scenes is a single node in existence. Any explanation why i needed the //?


Solution

  • You can use XPath:

    doc.select_node("/library_geometries//geometry[@id='value']")
    

    Note that this results in looking for this node in the entire subtree; for COLLADA it's frequently more efficient to build a map from string to node once and then use it for all lookups. IIRC ids have to be globally unique so you can just build the map for the entire tree once using code like this:

    unordered_map<string, xml_node> map;
    
    for (xpath_node item: doc.select_nodes("//@id"))
        map[item.attribute().value()] = item.parent();