XSLT: плоский XML для вложенного списка HTML - PullRequest
4 голосов
/ 03 июня 2011

Я новичок в XSLT. Я знаю, что мне нужно использовать xsl: for-each-group, но я не могу понять ничего, кроме основного списка. Будет ли какая-то рекурсия работать лучше? Подойдет любое решение XSLT 1.0 или 2.0.

Ниже приведен пример XML. Обратите внимание, что наиболее важным атрибутом для организации данных в древовидную структуру является @taxonomy. Другие атрибуты @taxonomyName и @level предоставляются в качестве необязательных вспомогательных атрибутов.

<?xml version="1.0" encoding="utf-8"?>
<documents>
    <document level="0" title="Root document test" taxonomy="" taxonomyName="" />
    <document level="1" title="Level one document test" taxonomy="\CategoryI" taxonomyName="CategoryI" />
    <document level="1" title="Level one document test #2" taxonomy="\CategoryII" taxonomyName="CategoryII" />
    <document level="2" title="Level two document test" taxonomy="\CategoryII\SubcategoryA" taxonomyName="SubcategoryA" />
    <document level="2" title="Level two document test #2" taxonomy="\CategoryII\SubcategoryA" taxonomyName="SubcategoryA" />
    <document level="3" title="Level three document test" taxonomy="\CategoryII\SubcategoryA\Microcategory1" taxonomyName="Microcategory1" />
    <document level="2" title="Level two, no level one test" taxonomy="\CategoryIII\SubcategoryZ" taxonomyName="SubcategoryZ" />
</documents>

Вот ожидаемый результат. (Обратите внимание, что в выводе нет необходимости делать отступы. Я сделал это здесь для удобства чтения.)

<ul>
    <li>Root document test</li>
    <li>CategoryI
        <ul>
            <li>Level one document test</li>
        </ul>
    </li>
    <li>CategoryII
        <ul>
            <li>Level one document test #2</li>
            <li>SubcategoryA
                <ul>
                    <li>Level two document test</li>
                    <li>Level two document test #2</li>
                    <li>Microcategory1
                        <ul>
                            <li>Level three document test</li>
                        </ul>
                    </li>
                </ul>
            </li>
        </ul>
    </li>
    <li>CategoryIII
        <ul>
            <li>SubcategoryZ
                <ul>
                    <li>Level two, no subcategory test</li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

Вот лучшее, что я могу сделать.

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:key name="contacts-by-taxonomy" match="document" use="@taxonomy" />
    <xsl:template match="documents">
        <ul>
            <xsl:for-each-group select="document" group-by="@taxonomy">
                <xsl:sort select="@taxonomy" />
                <li>
                    <h3><xsl:value-of select="current-grouping-key()"/></h3>
                    <ul>
                        <xsl:for-each select="current-group()">
                            <li><xsl:value-of select="@title"/></li>
                        </xsl:for-each>
                    </ul>
                </li>
            </xsl:for-each-group>
        </ul>
    </xsl:template>
</xsl:stylesheet>

Я буду продолжать пыхтеть, но буду вечно благодарен, если кто-нибудь сможет бросить мне спасательный жилет. Спасибо!

1 Ответ

5 голосов
/ 04 июня 2011

ОК, вот мое решение, наконец. :-) По сути, он повторяется через дерево, и на каждом уровне он делает для каждой группы group-by = "следующий уровень @taxonomy".

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
   <xsl:output method="html" indent="yes" />   

   <xsl:template match="documents">
      <ul>
         <xsl:call-template name="tree-depth-n">
            <xsl:with-param name="population" select="document"/>
            <xsl:with-param name="depth" select="0"/>
            <xsl:with-param name="taxonomy-so-far" select="''"/>
         </xsl:call-template>
      </ul>
   </xsl:template>

   <!-- This template is called with a population that are all descendants
      of the same ancestors up to level n. -->      
   <xsl:template name="tree-depth-n">
      <xsl:param name="depth" required="yes"/>
      <xsl:param name="population" required="yes"/>
      <xsl:param name="taxonomy-so-far" required="yes"/>
      <!-- output a <li> for each document that is a leaf at this level,
      and a <li> for each sub-taxon of this level. --> 
      <xsl:for-each-group select="$population"
                    group-by="string(tokenize(@taxonomy, '\\')[$depth + 2])">
         <xsl:sort select="@taxonomy" />
         <xsl:choose>
            <!-- process documents at this level. -->
            <xsl:when test="current-grouping-key() = ''">
              <xsl:for-each select="current-group()">
                 <li><xsl:value-of select="@title"/></li>
              </xsl:for-each>
            </xsl:when>
            <!-- process subcategories -->
            <xsl:otherwise>
               <li>
                  <h3><xsl:value-of select="current-grouping-key()"/></h3>
                  <ul>
                     <!-- recurse -->
                     <xsl:call-template name="tree-depth-n">
                        <xsl:with-param name="population" select="current-group()"/>
                        <xsl:with-param name="depth" select="$depth + 1"/>
                        <xsl:with-param name="taxonomy-so-far" 
                          select="concat($taxonomy-so-far, '\\', current-grouping-key())"/>
                     </xsl:call-template>                    
                  </ul>
              </li>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:for-each-group>      
   </xsl:template>
</xsl:stylesheet>

При заданном входе вывод:

<ul>
   <li>Root document test</li>
   <li>
      <h3>CategoryI</h3>
      <ul>
         <li>Level one document test</li>
      </ul>
   </li>
   <li>
      <h3>CategoryII</h3>
      <ul>
         <li>Level one document test #2</li>
         <li>
            <h3>SubcategoryA</h3>
            <ul>
               <li>Level two document test</li>
               <li>Level two document test #2</li>
               <li>
                  <h3>Microcategory1</h3>
                  <ul>
                     <li>Level three document test</li>
                  </ul>
               </li>
            </ul>
         </li>
      </ul>
   </li>
   <li>
      <h3>CategoryIII</h3>
      <ul>
         <li>
            <h3>SubcategoryZ</h3>
            <ul>
               <li>Level two, no level one test</li>
            </ul>
         </li>
      </ul>
   </li>
</ul>

То, что я верю, то, что вы хотели. (Я добавил туда <h3> s, как вы делали это при попытке XSL, для названий категорий, а не для заголовков документов.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...