Reputation: 10358
In this flat XML how can I get a 'immediate parent' of company with given name? I mean an element which has Level = 0 and is first element before given company.
So given that company name is V I need to get company Z.
<Companies>
<Company>
<Name>X</Name>
<Level>1</Level>
</Company>
<Company>
<Name>Y</Name>
<Level>1</Level>
</Company>
<Company>
<Name>Z</Name>
<Level>0</Level>
</Company>
<Company>
<Name>V</Name>
<Level>1</Level>
</Company>
</Companies>
This is not working?
<xsl:value-of select="Companies/Company[Name='V']/preceding-sibling[Level=0]/last()
Upvotes: 0
Views: 361
Reputation: 27995
In your <xsl:value-of select="...">
, you can change
Companies/Company[Name='V']/preceding-sibling[Level=0]/last()
to
(Companies/Company[Name='V']/preceding-sibling::*[Level=0])[last()]
Note:
::
and a node-test (such as *
).last()
is not what you want as a location step (though it might be legal in XSLT 2.0.... I'm not sure). Instead, you want it inside a predicate [
]
. The predicate [last()]
is shorthand for [position() = last()]
.[last()]
is a reason that @Lukasz alluded to: The preceding-sibling::
has a reverse direction.So if you say preceding-sibling::*[last()]
you get the last preceding sibling in reverse order, i.e. the first preceding sibling in document order! Since you actually want the last preceding sibling in document order, you can do preceding-sibling::*[1]
as @Lukasz did; or you can what I did with (...)[last()]
. Both are valid. I slightly prefer the latter, as I'll explain below.
The (...)[last()]
form works because the parentheses force the [last()]
predicate to apply to the whole XPath expression up to that point, instead of to just the step that has the preceding-sibling::
axis. As a result, the predicate is being applied to an expression that is not governed by the preceding-sibling::
axis' reverse direction. Therefore we select the last node (of the given set) in document order.
Without the parentheses, in some XPath expressions, the associativity of predicates and axes can come as a surprise. You have to know a bit about precedence rules. Admittedly I'm a bit lazy about learning the details of the precedence rules. But my experience (in many programming languages) has been that relying on less-obvious rules of parsing precedence is a good way to make sure your code gets misunderstood or messed up by the next person who comes along and tries to debug or modify it. (Often that "next person" is myself, a month later.)
Upvotes: 2
Reputation: 7662
The following XSLT fragment will retrieve the previous Company element from the provided sample XML:
<xsl:copy-of select="Companies/Company[Name='V']/preceding-sibling::*[1]" />
It would be:
<Company>
<Name>Z</Name>
<Level>0</Level>
</Company>
As you can see we're accessing the first node (*[1]
) - this might seem to be against intuition - you can find further explanation here.
EDIT: I'm not sure whether you want to have the Level=0 condition taken into consideration.
Upvotes: 1