我在确定节点集的正确上下文时遇到了一些麻烦。我有一个看起来有点像这样的模板匹配(使用XSL 2.0):
<xsl:template match="//chapter/body/*[matches(name(), '^toc')][crossref][not(crossref/@idref='cip' or crossref/@idref='copy')]">
<xsl:variable name="curr_id" select="crossref/@idref"/>
<xsl:element name="location">
<xsl:attribute name="id"><xsl:value-of select="$curr_id"/></xsl:attribute>
<xsl:attribute name="order"><xsl:value-of select="position()"/></xsl:attribute>
<xsl:element name="label">
<text><xsl:value-of select="."/></text>
</xsl:element>
</xsl:element>
</xsl:template>
XML看起来有点像这样:
<chapter id="toc">
<body>
<title>Contents</title>
<tocfm><crossref idref="cip">Catalog</crossref></tocfm>
<tocfm><crossref idref="copy">Copyright</crossref></tocfm>
<tocfm><crossref idref="ded">Dedication</crossref></tocfm>
<toc><crossref idref="prologue">Prologue</crossref></toc>
<toc><crossref idref="pt1">Book One</crossref></toc>
<toc><crossref idref="pt2">Book Two</crossref></toc>
<toc><crossref idref="pt3">Book Three</crossref></toc>
</body>
</chapter>
我的期望是谓词将生成一个包含以下内容的节点集:
<tocfm><crossref idref="ded">Dedication</crossref></tocfm>
<toc><crossref idref="prologue">Prologue</crossref></toc>
<toc><crossref idref="pt1">Book One</crossref></toc>
<toc><crossref idref="pt2">Book Two</crossref></toc>
<toc><crossref idref="pt3">Book Three</crossref></toc>
换句话说,包含一个idref不是cip或copy的crossref的所有类toc元素。模板在输出方面执行此操作,但位置函数似乎不在该节点集上。相反,它为奉献产生了一个'3'的位置。但是,如果我输出通过跟随[1]的谓词找到的节点的值,我会得到Dedication作为该值。所以,我对于正在发挥作用的立场感到难过。谁能开导我?
答案 0 :(得分:9)
您的期望是错误的,因为它基于对如何定义上下文序列的常见误解。上下文序列是 NOT 由模板匹配模式决定,而是由模板之外的东西确定。
在某些时候,使用select表达式调用xsl:apply-templates。评估此表达式以生成项序列。将当前上下文序列推送到堆栈上。该项目序列成为上下文序列。上下文序列按顺序迭代。在访问每个项目时,此项目成为上下文项目,position()函数反映上下文序列中上下文项目的位置。此时,根据匹配表达式和优先级找到适当的模板。匹配表达式不返回序列。它们只是对上下文项的布尔测试 - 上下文项与模板规则匹配,或者不是。如果匹配且具有最高优先级,则输入模板序列构造函数。此时,上下文序列,上下文项和位置()都由客户端xsl:apply-templates定义,而不是由匹配条件定义。退出序列构造函数后,控件返回客户端xsl:apply-templates。它将增加position()数,更新上下文项,并再次找到适当的模板。这可能与前一项或同一项相同。
此样式表说明了客户端指令(apply-templates,for-each等)确定了上下文序列和位置(),不是模板匹配模式。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<t>
<note message ="apply-templates on EVEN items">
<xsl:apply-templates select="*/item[ (@id mod 2) = 0]"/>
</note>
<note message ="apply-templates on ODD items">
<xsl:apply-templates select="*/item[ (@id mod 2) = 1]">
<xsl:sort select="position()" data-type="number" order="descending"/>
</xsl:apply-templates>
</note>
</t>
</xsl:template>
<xsl:template match="item[@colour='blue']">
<note message='Brought to you by the Blue template!' positon="{position()}">
<xsl:copy-of select='self::*'/>
</note>
</xsl:template>
<xsl:template match="item[@colour='red']">
<note message='Brought to you by the Red template!' positon="{position()}">
<xsl:copy-of select='self::*'/>
</note>
</xsl:template>
</xsl:stylesheet>
...适用于此文件......
<t>
<item id="1" colour="blue" />
<item id="2" colour="blue" />
<item id="3" colour="blue" />
<item id="4" colour="red" />
<item id="5" colour="red" />
<item id="6" colour="red" />
</t>
...产生这个输出......
<t>
<note message="apply-templates on EVEN items">
<note message="Brought to you by the Blue template!" positon="1">
<item id="2" colour="blue" />
</note>
<note message="Brought to you by the Red template!" positon="2">
<item id="4" colour="red" />
</note>
<note message="Brought to you by the Red template!" positon="3">
<item id="6" colour="red" />
</note>
</note>
<note message="apply-templates on ODD items">
<note message="Brought to you by the Red template!" positon="1">
<item id="5" colour="red" />
</note>
<note message="Brought to you by the Blue template!" positon="2">
<item id="3" colour="blue" />
</note>
<note message="Brought to you by the Blue template!" positon="3">
<item id="1" colour="blue" />
</note>
</note>
</t>
答案 1 :(得分:2)
position()
指的是context position。来自XSLT 2.0规范:
上下文位置是当前正在处理的项目序列中的上下文项的位置。只要上下文项更改,它就会更改。当使用诸如
xsl:apply-templates
或xsl:for-each
之类的指令来处理项目序列时,序列中的第一项处理的上下文位置为1,第二项目的上下文位置为2, ]上下文位置由XPath表达式position()
返回。
理解的关键是xsl:template
不会更改上下文项。它可能会被xsl:apply-templates
或xsl:for-each
更改。例如,请考虑以下代码:
<xsl:apply-templates select="tocfm|toc"/>
当它处理Dedication
节点时,该节点将成为上下文项,上下文位置成为输入序列中该节点的位置(所有tocfm
的并集和toc
元素。这或类似的东西可能就是你的3来自哪里。
正如apply-templates
part of the spec所说:
xsl:apply-templates
指令将节点序列(通常是源树中的节点)作为输入[...]如果指令有一个或多个xsl:sort
子节点,则输入序列按照描述进行排序在13排序。这种结果在下面称为排序顺序;如果没有xsl:sort
个元素,则排序的序列与输入序列相同。 [...]通过查找模式与该节点匹配的模板规则来处理输入序列中的每个节点。 [...]与排序序列中的第N个节点匹配的规则将使用该节点作为上下文项进行评估,其中N为上下文位置,并且排序序列的长度为上下文大小。
您可能会发现这个FAQ entry on position()
很有用(尽管它解释了XSLT 1.0概念current node list的问题,但未在XSLT 2.0中使用)。