XSLT: последовательность из более чем одного элемента не допускается в качестве первого аргумента concat ()

Я пытаюсь создать список страниц, который состоит из объединения идентификаторов различных div (@chapter) | (@part). Моя проблема связана с вложенными структурами: @part может содержать любое количество @chapter. Я хочу выбрать только идентификатор последнего предка номера страницы в порядке документа.

Ниже приведен фрагмент 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 (). Очевидно, мой XPath выбирает несколько строк в первом аргументе из-за @part и @chapters. Как ограничить выбор первым предком 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>

person user2093335    schedule 04.12.2013    source источник


Ответы (2)


Прежде всего, вертикальная черта в 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 обрабатываете строку и создаете атрибут, вы можете просто заключите его в фигурные скобки, чтобы сделать XSLT короче, как показано ниже:

<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: Если вы хотите продолжить использование 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>

Он немного более подробный, поэтому я предпочитаю встроенные значения атрибутов .

person Community    schedule 04.12.2013
comment
Спасибо за прекрасное объяснение! Также спасибо за совет по более короткому XSLT. Если я использую конструкцию ‹xsl: element› с атрибутом select, это не похоже на допустимый XPath. Однако он хорошо работает с самим элементом. - person user2093335; 05.12.2013
comment
@ user2093335 Не беспокойтесь. Но что теперь не является легальным Xpath? - person ; 05.12.2013
comment
Если я использую say: ‹xsl: element name = a› ‹xsl: attribute name = href select = {$ html} xhtml # page {$ pagenumber} /› ‹xsl: value-of select = $ pagenumber /› ‹/ xsl : element ›- Я получаю сообщение об ошибке при выборе атрибута xsl: - person user2093335; 05.12.2013
comment
О, да, вы можете использовать {curly braces} только при создании встроенных атрибутов, как это сделал я. Добавлю ответ, как это сделать с обычными xsl:attributes. - person ; 05.12.2013

Я бы посоветовал вам изменить вычисление переменной html на

<xsl:variable name="html" select="concat(ancestor::div[@class='chapter' or @class='part'][1]/@id, '.xhtml#page')"/>

Переместив проверку на действительный class в скобки, вы никогда не получите больше одного узла из выражения XPath-предка.

person Marcus Rickert    schedule 04.12.2013