Как выбрать последние N элементов с помощью XPath?

Я поддерживаю веб-сайт, который генерирует XML-контент, который затем транслируется на веб-страницы с использованием XSLT. Меня попросили создать новую таблицу стилей, которая преобразует вывод страницы "архив" в Atom для синдикации. Проблема, с которой я сталкиваюсь, заключается в том, что страница архива содержит довольно большое количество элементов - 142 и подсчет - и в фиде никогда не должно быть более тридцати элементов.

В настоящее время вывод с страницы архива выглядит примерно так:

<archive>
    <year>
        <month>
            <day>
            <day>
            ...
        </month>

        ...
    </year>

    ...
</archive>

Теги year и month используются преобразованием HTML, но совершенно неактуальны для фида Atom. Я надеялся, что использование функции position() с осью потомка будет работать (//day[position()>last()-30]), но это выбирает последние 30 дней каждого месяца, что совсем не то, что мне нужно.: -)

Есть ли способ сделать это с помощью XSLT или XPath? Необходимость модифицировать генератор XML для добавления, скажем, атрибута feed="true" за последние тридцать дней, кажется довольно неприятным kludge.

Ответ 1

position()/last() возвращает позицию/последнюю позицию в текущем контексте, поэтому, когда навигатор помещается за один месяц > , позиция() вернет < день > в течение этого месяца и last() вернет последний < день > в течение этого месяца, но я думаю, вы это знаете.

Следовательно, что вы можете сделать, это сгладить все <day> в массиве и поместить в переменную до выбора так же, как вы делали раньше.

<xsl:variable name="days" select="//day"/>
<xsl:apply-templates select="$days[position()>last()-30]" />

Ответ 2

Просматривая XSLT spec сегодня, я нашел примечание, которое объясняет, почему // ведет себя следующим образом:

// не подходит для /descendant-or-self::node()/. Например, //para является коротким для /descendant-or-self::node()/child::para и поэтому выберет любой элемент para в документе (даже элемент para, который является элементом документа, будет выбран //para, поскольку элемент документа node является потомком корня node); div//para сокращен для div/descendant-or-self::node()/child::para и поэтому выберет всех para потомков дочерних div.

ПРИМЕЧАНИЕ. Путь местоположения //para[1] не означает то же, что и путь местоположения /descendant::para[1]. Последний выбирает первый элемент потомка para; первый выбирает все дочерние элементы para, которые являются первыми детьми para их родителей.

Другими словами, при использовании //, position() вычисляется вдоль оси child, а не оси descendant-or-self. Указание descendant или descendant-or-self позволяет вам получить первые/последние n узлов, как вы ожидали:

<xsl:apply-templates select="descendant::day[position()>last()-30]"/>