XSLT: find number of given node
I have an XML structure representing a logical derivation, and it looks like this:
<derivation>
<step name="foo">
<derive>[...]</derive>
<by>[...]</by>
</step>
<step name="bar">
<derive>[...]</derive>
<by>
<from name="foo"/>, conjunction elimination
</by>
</step>
<step name="baz">
<derive>[...]</derive>
<by>
<from name="bar"/>, existential quantification
</by>
</step>
[...]
<开发者_开发知识库;/derivation>
Each <step>
in the derivation has a number -- for instance, that with name="foo"
would be number 1
, that with name="bar"
would be number 2
, that with name="baz"
would be number 3
, and so on. When inside the <step>
, I can find the number with <xsl:number/>
. Good so far.
Now, where an element <from name="bar"/>
occurs, I want it to be replaced by the number of the <step>
element with name="bar"
. There are three subproblems to solve here:
- Find the most recent ancestor of the
<from>
element that is a<derivation>
. - From that, find the first child that is a
<step>
element withname="bar"
. In the above instance, this would find the second child of the<derivation>
. - Identify the number of that element. In the above, that would be
2
.
Can someone tie together solutions to these subproblems to satisfy my requirement?
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="from[@name]">
<xsl:variable name="vReferred" select=
"ancestor::derivation[1]/step[@name = current()/@name]"/>
<xsl:for-each select="$vReferred">
<xsl:number count="step" level="any"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<derivation>
<step name="foo">
<derive>[...]</derive>
<by>[...]</by>
</step>
<step name="bar">
<derive>[...]</derive>
<by>
<from name="foo"/>, conjunction elimination
</by>
</step>
<step name="baz">
<derive>[...]</derive>
<by>
<from name="bar"/>, existential quantification
</by>
</step> [...]
</derivation>
produces the wanted, correct result:
<derivation>
<step name="foo">
<derive>[...]</derive>
<by>[...]</by>
</step>
<step name="bar">
<derive>[...]</derive>
<by>1, conjunction elimination
</by>
</step>
<step name="baz">
<derive>[...]</derive>
<by>2, existential quantification
</by>
</step> [...]
</derivation>
Fun with XPath axes...
count(ancestor::derivation/step[@name=$name]/preceding-sibling::step)+1
This grabs the nearest ancestor that is a derivation, gets it's child step with the correct name, and then gets all preceding siblings named step. It counts them and adds 1 (since step 2 would have 1 preceding sibling).
XPath axis reference
Another solution, using keys (may be more efficient if on average there are more than one references to the same step):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kStepByName" match="derivation/step"
use="@name"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="from[@name]">
<xsl:variable name="vReferred" select=
"key('kStepByName',@name)"/>
<xsl:for-each select="$vReferred">
<xsl:number count="step" level="any"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
While the two solutions already posted are both okay, just for the reference I'm adding another possible solution.
Something like :
<xsl:template match="from">
<xsl:variable name="ref" select="@name"/>
<xsl:apply-templates mode="number" select="ancestor::derivation/step">
<xsl:with-param name="ref" select="$ref"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template name="step" mode="number">
<xsl:param name="ref"/>
<xsl:if test="@name=$ref">
Item no. <xsl:value-of select="position()"/>
</xsl:if>
</xsl:template>
Please note that I haven't been writing XSLT for quite a few years now, while the idea behind this solution should be correct, the syntax may be completely wrong.
精彩评论