HTML-списки - несколько вложенных XSLT для каждого цикла - PullRequest
3 голосов
/ 03 февраля 2012

Я пытаюсь создать многоуровневый вложенный список html из xml / xsl.

Например, предпочтительный вывод html будет выглядеть следующим образом:

<ul>
 <li>Level 1 - Item 1</li>
    <ul>
        <li>Level 2 - Item 1-1</li>
        <li>Level 2 - Item 1-2</li>
    </ul>

<li> Level 1 - Item 2</li>
    <ul>
        <li>Level 2 - Item 2-1
            <ul>
                <li>Level 3 - Item 2-1-1</li>
                <li>Level 3 - Item 2-1-2</li>
                <li>Level 3 - Item 2-1-3</li>
            </ul>
        </li>
        <li>Level 2 - Item 2-2
            <ul>
                <li>Level 3 - Item 2-2-1</li>
                <li>Level 3 - Item 2-2-2</li>
            </ul>
        </li>
</ul>

XML:

<doc>

    <item>
        <one>Level 1 - Item 1</one>
            <two>Level 2 - Item 1-1</two>
            <two>Level 2 - Item 1-2</two>
    </item>

    <item>
        <one>Level 2 - Item 2</one>
            <two>Level 2 - Item 2-1</two>
                <three>Level 3 - Item 2-1-1</three>
                <three>Level 3 - Item 2-1-2</three>
                <three>Level 3 - Item 2-1-3</three>
            <two>Level 2 - Item 2-2</two>
                <three>Level 3 - Item 2-2-1</three>
                <three>Level 3 - Item 2-2-2</three> 
    </item>

</doc>

Моя неудачная попытка XSL:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
    <html>
    <body>
    <xsl:for-each select="doc/item">
    <li><xsl:value-of select="one" />
    <ul>
    <xsl:for-each select="two">
    <li><xsl:value-of select="."/>
    <xsl:for-each select="../three"><ul><li><xsl:value-of select="."/></li></ul></xsl:for-each>
    </li>
    </xsl:for-each>
    </ul>
    </li>
    </xsl:for-each>
    </body>
    </html>
    </xsl:template>
    </xsl:stylesheet>

Это то, что я получаю ниже ... Обратите внимание, что когда есть предмет уровня 3, все предметы объединяются, а затем отображаются под обоими.

    <li>Level 1 - Item 1<ul>
    <li>Level 2 - Item 1-1</li>
    <li>Level 2 - Item 1-2</li>
    </ul>
    </li>
    <li>Level 2 - Item 2<ul>
    <li>Level 2 - Item 2-1<ul>
    <li>Level 3 - Item 2-1-1</li>
    </ul>

    <ul>
    <li>Level 3 - Item 2-1-2</li>
    </ul>
    <ul>
    <li>Level 3 - Item 2-1-3</li>
    </ul>
    <ul>
    <li>Level 3 - Item 2-2-1</li>
    </ul>
    <ul>
    <li>Level 3 - Item 2-2-2</li>
    </ul>
    </li>

    <li>Level 2 - Item 2-2<ul>
    <li>Level 3 - Item 2-1-1</li>
    </ul>
    <ul>
    <li>Level 3 - Item 2-1-2</li>
    </ul>
    <ul>
    <li>Level 3 - Item 2-1-3</li>
    </ul>
    <ul>
    <li>Level 3 - Item 2-2-1</li>
    </ul>

    <ul>
    <li>Level 3 - Item 2-2-2</li>
    </ul>
    </li>
    </ul>
    </li>

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

спасибо!

Ответы [ 3 ]

4 голосов
/ 03 февраля 2012

Попробуйте ниже. Объяснение: выберите всех «трех» братьев и сестер, у которых перед ним стоят одинаковые первые «два» брата. Я запустил это в XML Spy и получил желаемый результат (см. Ниже xslt).

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:template match="/">
        <html>
            <body>
                <xsl:for-each select="doc/item">
                    <li>
                        <xsl:value-of select="one" />
                        <ul>
                            <xsl:for-each select="two">
                                <li>
                                    <xsl:value-of select="."/>
                                    <ul>
                                        <xsl:for-each select="following-sibling::three[preceding-sibling::two[1]=current()]">
                                                <li>
                                                    <xsl:value-of select="."/>
                                                </li>
                                        </xsl:for-each>
                                    </ul>
                                </li>
                            </xsl:for-each>
                        </ul>
                    </li>
                </xsl:for-each>
            </body>
        </html>
    </xsl:template>
    </xsl:stylesheet>

Выход:

<html>
    <body>
        <li>Level 1 - Item 1
            <ul>
                <li>Level 2 - Item 1-1
                    <ul></ul>
                </li>
                <li>Level 2 - Item 1-2
                    <ul></ul>
                </li>
            </ul>
        </li>
        <li>Level 2 - Item 2
            <ul>
                <li>Level 2 - Item 2-1
                    <ul>
                        <li>Level 3 - Item 2-1-1</li>
                        <li>Level 3 - Item 2-1-2</li>
                        <li>Level 3 - Item 2-1-3</li>
                    </ul>
                </li>
                <li>Level 2 - Item 2-2
                    <ul>
                        <li>Level 3 - Item 2-2-1</li>
                        <li>Level 3 - Item 2-2-2</li>
                    </ul>
                </li>
            </ul>
        </li>
    </body>
</html>
4 голосов
/ 03 февраля 2012

Вот решение XSLT 1.0, которое работает с вашим входным XML.

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:my="http://tempuri.org"
  exclude-result-prefixes="my"
>
  <xsl:output indent="yes" />

  <!-- define which elements are where in the hierarchy -->
  <my:level name="one"   higher="" deeper="two,three" />
  <my:level name="two"   higher="one" deeper="three"  />
  <my:level name="three" higher="one,two" deeper="" />

  <xsl:template match="doc">
    <body>
      <xsl:apply-templates mode="ul" select="item/*[1]" />
    </body>
  </xsl:template>

  <xsl:template match="one|two|three" mode="ul">
    <ul>
      <xsl:apply-templates mode="li" select="." />
    </ul>
  </xsl:template>

  <xsl:template match="one|two|three" mode="li">
    <xsl:variable name="myName" select="name()" />
    <xsl:variable name="myID"   select="generate-id()" />
    <!-- select the appropriate hierarchy info for this node -->
    <xsl:variable name="level"  select="
      document('')/*/my:level[@name = $myName]
    " />
    <li>
      <xsl:value-of select="." />
      <!-- create <ul> if immediately follwing sibling is deeper -->
      <xsl:apply-templates mode="ul" select="
        following-sibling::*[1][contains($level/@deeper, name())]
      " />
    </li>
    <!-- process contiguous following siblings of same level -->
    <xsl:apply-templates mode="li" select="
      following-sibling::*[name() = $myName][
        generate-id(
          preceding-sibling::*[contains($level/@higher, name())][1]/following-sibling::*[1]
        ) 
        = $myID
      ]
    " />
  </xsl:template>

</xsl:stylesheet>

Исходя из входного документа по вашему вопросу, он выводит:

<body>
  <ul>
    <li>Level 1 - Item 1
      <ul>
        <li>Level 2 - Item 1-1</li>
        <li>Level 2 - Item 1-2</li>
      </ul>
    </li>
  </ul>
  <ul>
    <li>Level 2 - Item 2
      <ul>
        <li>Level 2 - Item 2-1
          <ul>
            <li>Level 3 - Item 2-1-1</li>
            <li>Level 3 - Item 2-1-2</li>
            <li>Level 3 - Item 2-1-3</li>
          </ul>
        </li>
        <li>Level 2 - Item 2-2
          <ul>
            <li>Level 3 - Item 2-2-1</li>
            <li>Level 3 - Item 2-2-2</li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</body>

Честно говоря, я слишком устал сейчас, чтобы подробно объяснить решение. Я оставил несколько комментариев, хотя. Достаточно сказать, что это довольно сложно.

Если ваш XML будет выглядеть так (т.е. правильно вложен):

<doc>
  <item title="Level 1 - Item 1">
    <item title="Level 2 - Item 1-1" />
    <item title="Level 2 - Item 1-2" />
  </item>
  <item title="Level 2 - Item 2">
    <item title="Level 2 - Item 2-1">
      <item title="Level 3 - Item 2-1-1" />
      <item title="Level 3 - Item 2-1-2" />
      <item title="Level 3 - Item 2-1-3" />
    </item>
    <item title="Level 2 - Item 2-2">
      <item title="Level 3 - Item 2-2-1" />
      <item title="Level 3 - Item 2-2-2" />
    </item>
  </item>
</doc>

решение, которое даст тот же HTML-результат, что и выше, будет выглядеть так:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output indent="yes" />

  <xsl:template match="doc">
    <body>
      <xsl:for-each select="item">
        <ul>
          <xsl:apply-templates select="." />
        </ul>
      </xsl:for-each>
    </body>
  </xsl:template>

  <xsl:template match="item">
    <li>
      <xsl:value-of select="@title" />
      <xsl:if test="item">
        <ul>
          <xsl:apply-templates select="item" />
        </ul>
      </xsl:if>
    </li>
  </xsl:template>
</xsl:stylesheet>
2 голосов
/ 03 февраля 2012

Это простое (без встроенного XML, без document() и contains() функций) короткое и эффективное преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:strip-space elements="*"/>

 <xsl:key name="kFollowing" match="two"
  use="generate-id(preceding-sibling::one[1])"/>

 <xsl:key name="kFollowing" match="three"
  use="generate-id(preceding-sibling::two[1])"/>

 <xsl:template match="/*">
  <ul>
    <xsl:apply-templates select="item/one" mode="inGroup"/>
  </ul>
 </xsl:template>

 <xsl:template match="one|two" mode="inGroup">
  <li><xsl:value-of select="concat(., '&#xA;')"/>
    <xsl:variable name="vGroup" select=
        "key('kFollowing', generate-id())"/>
    <xsl:apply-templates select=
       "$vGroup[1]">
     <xsl:with-param name="pGroup" select="$vGroup"/>
    </xsl:apply-templates>
  </li>
 </xsl:template>

 <xsl:template match="two|three">
  <xsl:param name="pGroup"/>

  <xsl:if test="position() = 1">
     <ul>
      <xsl:apply-templates select="$pGroup" mode="inGroup"/>
     </ul>
  </xsl:if>
 </xsl:template>

 <xsl:template match="three" mode="inGroup">
  <li><xsl:value-of select="."/></li>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному XMLдокумент :

<doc>
    <item>
        <one>Level 1 - Item 1</one>
        <two>Level 2 - Item 1-1</two>
        <two>Level 2 - Item 1-2</two>
    </item>
    <item>
        <one>Level 2 - Item 2</one>
        <two>Level 2 - Item 2-1</two>
        <three>Level 3 - Item 2-1-1</three>
        <three>Level 3 - Item 2-1-2</three>
        <three>Level 3 - Item 2-1-3</three>
        <two>Level 2 - Item 2-2</two>
        <three>Level 3 - Item 2-2-1</three>
        <three>Level 3 - Item 2-2-2</three>
    </item>
</doc>

дает требуемый, правильный результат :

<ul>
    <li>Level 1 - Item 1
        <ul>
            <li>Level 2 - Item 1-1
            </li>
            <li>Level 2 - Item 1-2
            </li>
        </ul>
    </li>
    <li>Level 2 - Item 2
        <ul>
            <li>Level 2 - Item 2-1
                <ul>
                    <li>Level 3 - Item 2-1-1</li>
                    <li>Level 3 - Item 2-1-2</li>
                    <li>Level 3 - Item 2-1-3</li>
                </ul>
            </li>
            <li>Level 2 - Item 2-2
                <ul>
                    <li>Level 3 - Item 2-2-1</li>
                    <li>Level 3 - Item 2-2-2</li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

и отображается браузером как :

  • Уровень 1 - Элемент 1
    • Уровень 2 - Элемент 1-1
    • Уровень 2 - Элемент 1-2
  • Уровень 2 - Элемент 2
    • Уровень 2 - Элемент 2-1
      • Уровень 3 - Элемент 2-1-1
      • Уровень 3 - Элемент 2-1-2
      • Уровень 3 - Элемент 2-1-3
    • Уровень 2 - Элемент 2-2
      • Уровень 3 - Элемент 2-2-1
      • Уровень 3 - Элемент 2-2-2

Объяснение :

  1. Существует один ключ kFollowing (с двумя отдельными определениями), который индексирует любой элемент two или three по значению generate-id() его логического родителя (соответственно one или * 1065)*).Это помогает нам иметь один шаблон, соответствующий элементам one и two.

  2. Каждый первый в группе (two или three) элемент сопоставляется иобрабатывается без режима.В этом шаблоне создается оболочка ul, затем все элементы в группе (переданные в качестве параметра) обрабатываются в режиме с именем inGroup.

...