XSLT:不允许包含多个项目的序列作为concat()的第一个参数

时间:2013-12-04 22:44:45

标签: xslt xpath xslt-2.0

我正在尝试生成一个页面列表,其中包含各种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>

2 个答案:

答案 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表达式获得更多的那个节点。