Двойная итерация по набору узлов в XSLT-1.0

#xpath #xslt-1.0 #exslt

#xpath #xslt-1.0 #exslt

Вопрос:

У меня есть XML, содержащий записи в блоге. Каждая запись содержит date узел.

Я хочу извлечь список лет, за которые были сделаны сообщения, и распечатать их в необычном формате. Для каждого года я хочу распечатать список активных месяцев очень похожим образом.

Внешняя часть у меня работает нормально. Поскольку я не смог найти способ достичь этого с помощью необработанного XSLT-1.0, я реализовал его с помощью EXSLT. Я перебираю записи, извлекаю год, помещаю его в новый year узел и сохраняю это как временное дерево XML в переменной. Затем я использую EXSLT для создания набора узлов из этой переменной, повторяю его и удаляю дубликаты.

Однако, как только я вызываю внутренний шаблон для каждого года, даже если я скопирую и вставлю код построения переменной из первого шаблона и дамп его, он не генерирует никаких выходных данных. Он просто отказывается выполнять повторную итерацию по тому же набору узлов.

Я упираюсь в стену здесь, и я определенно не могу понять, что именно происходит.

Это все, что мне удалось упростить таблицу стилей::

 <xsl:param name="myparam" select="''" />

<xsl:template match="/">
    <ul>
        <xsl:call-template name="outer" />
    </ul>
</xsl:template>

<xsl:template name="outer">
    <xsl:variable name="years_tree">
        <xsl:for-each select="//post">
            <xsl:sort select="date" />
            <xsl:element name="year"><xsl:value-of select="substring(date, 1, 4)" /></xsl:element>
        </xsl:for-each>
    </xsl:variable>

    <xsl:comment>
        <xsl:copy-of select="$years_tree" />
    </xsl:comment>

    <xsl:for-each select="exsl:node-set($years_tree)/year[not(.=following::year)]">
        <li>
            <xsl:value-of select="." />
            <xsl:if test="starts-with($myparam, string(.))">
                <ul>
                    <xsl:call-template name="inner" />
                </ul>
            </xsl:if>
        </li>
    </xsl:for-each>
</xsl:template>

<xsl:template name="inner">
    <xsl:variable name="years_tree">
        <xsl:for-each select="//post">
            <xsl:sort select="date" />
            <xsl:element name="year"><xsl:value-of select="substring(date, 1, 4)" /></xsl:element>
        </xsl:for-each>
    </xsl:variable>

    <xsl:copy-of select="$years_tree" />
</xsl:template>

</xsl:stylesheet>
  

Это мой пример ввода::

 <?xml version="1.0" encoding="UTF-8"?>
<blog>
    <post>
        <date>2011-10-22T22:50:26</date>
    </post>
    <post>
        <date>2011-10-02T17:25:14</date>
    </post>
    <post>
        <date>2011-10-14T11:58:58</date>
    </post>
    <post>
        <date>2011-11-21T11:58:58</date>
    </post>
    <post>
        <date>2010-10-14T11:58:58</date>
    </post>
    <post>
        <date>2011-09-14T11:58:58</date>
    </post>
</blog>
  

и это результат, который я получаю::

 <?xml version="1.0"?>
<ul>
  <debug>
    <year>2010</year>
    <year>2011</year>
    <year>2011</year>
    <year>2011</year>
    <year>2011</year>
    <year>2011</year>
  </debug>
  <li>2010<ul><debug/></ul></li>
  <li>2011<ul><debug/></ul></li>
</ul>
  

Ответ №1:

Ну, путь / выбирает узел документа текущего контекстного узла, и в вашем случае вы работаете с двумя документами, основным входным документом и временным документом, который вы создаете. Когда вы вызываете другой шаблон, контекстный узел является year элементом во временном документе, поэтому ваша попытка с помощью //post пытается найти post элементы во временном документе, а у вас их нет в этом документе. Поэтому вам нужно убедиться, что вы сохраняете глобальный, <xsl:variable name="main-doc" select="/"/> а затем, когда вы ищете post элементы в основном входном документе, вы можете использовать $main-doc//post .

Комментарии:

1. О боже, большое тебе спасибо. Я думаю, с моей стороны это было просто плохое понимание того, что делает // селектор. Ваш ответ хорошо объясняет это.