Vlax
Vlax

Reputation: 1537

XSLT 2.0: Limit the ancestor axes to a certain element/s level up the document tree

I'm seeing a quite odd behaviour, when trying to limit the results given by applying ancestor::* to an element I always get an extra ancestor although is expressly excluded by the predicate.

Here the code:

XML:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <level_a> 
    <level_b> 
      <level_c> 
        <level_d> 
          <level_e/> 
        </level_d> 
      </level_c> 
    </level_b> 
  </level_a>
  <level_b> 
    <level_c> 
      <level_d> 
        <level_d> 
          <level_e/> 
        </level_d> 
      </level_d> 
    </level_c> 
  </level_b>
</root>

XPath:

(//level_d[not(level_d)])[last()]/ancestor::*[level_c|level_b] 

so basically I'm selecting the level_d elements that doesn't have another level_d element nested, getting the last one of them and trying to get all the ancestors up to element level_b. But the result I'm seeing using Altova XMLSpy 2011 is:

I don't quite understand why I'm getting that result and how can I improve my xpath to limit effectively the ancestors up to level_b (i.e. level_c and level_b).

Any hint is greatly appreciated!
Regards
Vlax

Upvotes: 1

Views: 662

Answers (2)

Jirka Š.
Jirka Š.

Reputation: 3428

I think you get right result because clause ancestor::*[level_c|level_b] I read as "all ancestors containing element level_b or level_c". So, level_b is ok because it contains level_c and level_a is ok too because it contains level_b.

So if I change your XPath into (//level_d[not(level_d)])[last()]/ancestor::*[level_c] it results into level_b only.

Probably it is not exactly what you asking for but I'm not sure if I understand well the purpose of your XPath :-)

Upvotes: 2

Martin Honnen
Martin Honnen

Reputation: 167716

Well ancestor::*[level_c|level_b] selects all elements on the ancestor axis that have a level_c or level_b child.

You might want (//level_d[not(level_d)])[last()]/ancestor::*[self::level_c|self::level_b].

Or with your textual description "to limit effectively the ancestors to level_b" you simply want (//level_d[not(level_d)])[last()]/ancestor::level_b.

Upvotes: 2

Related Questions