我正在尝试生成一个页面列表,其中包含各种div(@chapter)|(@part)的id的串联。我的问题源于嵌套结构:@part可以包含任意数量的@chapter。我只想按文档顺序选择页码的最后一个祖先的id。
以下是XML的摘录:
<text>
<div class="part" id="s9781483352947.i870">
<a class="page" id="pbr-31"/>
<h1 class="title">Part 1</h1>
<p>This is a paragraph.</p>
<p>This is a paragraph.</p>
<div class="chapter" id="s9781483352947.n3.i884">
<a class="page" id="pbr-34"/>
<h1 class="title">Chapter 1</h1>
<p>This is a paragraph</p>
</div>
</div>
</text>
以下是我的XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="/">
<xsl:element name="nav">
<xsl:element name="h1">
<xsl:value-of select="'Pages'"/>
</xsl:element>
<xsl:element name="ol">
<xsl:apply-templates mode="pagelist" select="//a[@class='page']"/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="a[@class='page']" mode="pagelist">
<xsl:variable name="html" select="concat(ancestor::div[@class='chapter'][1]/@id | ancestor::div[@class='part'][1]/@id,'.xhtml#page')"/>
<xsl:variable name="pagenumber" select="substring-after(@id,'-')"/>
<xsl:element name="li">
<xsl:element name="a">
<xsl:attribute name="href" select="concat($html,$pagenumber)"/>
<xsl:value-of select="substring-after(@id,'-')"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
但是,我不断收到错误消息:不允许多个项目的序列作为concat()的第一个参数。显然,由于@part和@chapters,我的XPath在第一个参数中选择了多个字符串。如何将选择限制在class ='page'的第一个祖先?
这是上述样本的理想输出:
<nav>
<h1>Pages</h1>
<ol>
<li>
<a href="s9781483352947.i870.xhtml#page31">31</a>
</li>
<li>
<a href="s9781483352947.n3.i884.xhtml#page34">34</a>
</li>
</ol>
</nav>
答案 0 :(得分:4)
首先,Xpath中的管道符号不是您正在考虑的“或”。它的作用结合了两个独立的查询并将它们作为一个节点集返回。所以发生了什么,你对章和部分的每个查询都返回一个元素(第一个节点的@id属性),然后它组合返回一个带有两个节点的节点集。因此concat
有问题的原因。
你可以这样做:
(ancestor::div[@class='chapter'] | ancestor::div[@class='part'])[1]/@id
但这是错误的!因为这将获得组合系列中的第一个,但它不会将它们合并到正确的文档顺序中。因此,在获得参与之前,总是获得章节。
你需要的是:
ancestor::div[@class='chapter' or @class='part'][1]/@id
我不确定您为什么在已经concat
编辑的内容上使用concat
。顺便说一句,如果你concat
使用字符串并创建属性you can just surround it in curly braces to make the XSLT shorter,如下所示:
<xsl:template match="a[@class='page']" mode="pagelist">
<xsl:variable name="html" select="ancestor::div[@class='chapter' or @class='part'][1]/@id"/>
<xsl:variable name="pagenumber" select="substring-after(@id,'-')"/>
<li>
<a href="{$html}.xhtml#page{$pagenumber}">
<xsl:value-of select="$pagenumber"/>
</a>
</li>
</xsl:template>
编辑:如果您想继续使用xsl:attribute
元素,可以使用以下模板中的concat:
<xsl:template match="a[@class='page']" mode="pagelist">
<xsl:variable name="html" select="ancestor::div[@class='chapter' or @class='part'][1]/@id"/>
<xsl:variable name="pagenumber" select="substring-after(@id,'-')"/>
<li>
<a>
<xsl:attribute name="href">
<xsl:value-of select="concat($html,'.xhtml#page',$pagenumber)"/>
</xsl:attribute>
<xsl:value-of select="$pagenumber"/>
</a>
</li>
</xsl:template>
它有点冗长,这就是我更喜欢inline attribute values的原因。
答案 1 :(得分:2)
我建议您将变量html
的计算更改为
<xsl:variable name="html" select="concat(ancestor::div[@class='chapter' or @class='part'][1]/@id, '.xhtml#page')"/>
通过将有效class
的检查移动到括号中,您将永远不会从祖先XPath表达式获得更多的那个节点。