XSLT: A sequence of more than one item is not allowed as the first argument of concat()
xpath, xslt, xslt-2.0
Solution
First of all, the pipe symbol in Xpath isn't the "or" you are thinking of. What it does it combines two separate querys and returns them as one node-set. So what is happening, each of your queries for chapters and parts is returning one element (the @id attribute of the first node), and then its combined returning a node-set with two nodes. Hence why `concat` is having issues.
You could do:
(ancestor::div[@class='chapter'] | ancestor::div[@class='part'])[1]/@id
But it would be wrong!! As this will get the first in the combined series, but it won't merge them into the correct document order. So it would always get a chapter if available before getting a part.
What you need instead is this:
ancestor::div[@class='chapter' or @class='part'][1]/@id
I'm not sure why you are using `concat` on something you've already `concat`ed. As an aside if you are `concat`ing with a string and making an attribute, you can just surround it in curly braces to make the XSLT shorter, as in the below:
<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>
edit: If you wanted to continue using `xsl:attribute` elements you could use concat like in the following template:
<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>
Its a little bit more verbose, which is why I prefer inline attribute values.
Problem
I am trying to generate a pagelist which consists of a concatenation of the id of various divs (@chapter)|(@part). My problem stems from nested structures: @part can contain any number of @chapter. I only want to select the id of the last ancestor of a page number in document order. Below is a snippet of the 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> ``` And below is my 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> ``` However, I keep getting the error message: A sequence of more than one item is not allowed as the first argument of concat(). Obviously my XPath is selecting multiple strings in the first argument due to the @part and @chapters. How do I limit the selection to the first ancestor of a class='page'? This is the desired ouput for the above sample: ``` <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> ```