Search code examples
xpathxmlstarletxmllint

Can I query for a path through an XML document instead of just the leaf?


When I query an XML document with an XPATH, I only get the matched node(s) as a result. Is there a way to get the matched node(s) together with their parents up until root?

Lets take this very minimal XML example:

<myStuff>
  <room name="Kitchen">
    <cupboard name="Yellow Cupboard">
      <shelf name="Upper Shelf">
        <compartment name="Left Compartment">
          <cup name="Shiny cup" />
        </compartment>
      </shelf>
    </cupboard>
  </room>
</myStuff>

When I now want to find the Shiny cup, I can query (eg. using xmllint or xmlstarlet) for the XPATH "//cup[@name='Shiny cup']", but then I only found it in the document, not in the room itself. Is there a way to find the target the XPATH in the document and then output [an attribute of] all parent nodes to the root?

Like querying for

"//cup[@name='Shiny cup']/<for it and each parent thats not myStuff, give @name>"

that leads to

"Shiny cup",
"Left Compartment",
"Upper Shelf",
"Yellow Cupboard",
"Kitchen"

? Since it is not possible to say how many steps there are, I cannot add those statically to the XPATH itself, so the solution seems to have some sort of loop or recursion applied.


Solution

  • You could walk along the ancestor-or-self axis, e.g. using xmlstarlet:

    xmlstarlet sel -t -m '//cup[@name="Shiny cup"]' -v 'ancestor-or-self::*/@name'
    
    Kitchen
    Yellow Cupboard
    Upper Shelf
    Left Compartment
    Shiny cup