【问题标题】:HTML Lists - XSLT Multiple Nested For Each LoopHTML 列表 - 每个循环的 XSLT 多个嵌套
【发布时间】:2012-02-02 21:00:38
【问题描述】:

我正在尝试从 xml/xsl 生成多级嵌套 html 列表。

例如,首选的 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 示例以帮助其他人。

谢谢!

【问题讨论】:

  • 令我惊讶的是,人们试图用平面 XML 表达嵌套结构,然后最终挠头想知道如何将平面 XML 再次变为嵌套结构。如果您可以对那个 XML 做任何事情,,从那里开始。 XML 结构没有意义。
  • 对我来说,这听起来像是一个“请向我发送密码”问题。
  • @Michael:我已经在研究解决方案,请耐心等待。尽管如此,我的第一条评论是正确的。 XML 根本没有用处,如果你有机会改变它,你应该改变它。
  • 谢谢 - 我可以更改其中任何一个.. 供个人使用/学习.. 我只是想要最终结果输出.. 非常感谢您的帮助.. 我是 xsl/xml 的新手.
  • 如果可以更改,请正确嵌套 XML,而不是将 &lt;one&gt;&lt;two&gt;&lt;three&gt; 节点都放在同一级别。为了这个问题,我将发布一个可以与您的 XML 一起使用的解决方案。当你看到它时,我相信你会明白我的意思。

标签: html xml xslt nested-lists html-lists


【解决方案1】:

这是一个适用于您的输入 XML 的 XSLT 1.0 解决方案。

<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>

坦率地说,我现在太累了,无法详细解释解决方案。不过,我留下了一些cmets。可以说它非常复杂。

如果您的 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】:

试试下面的。 说明:选择所有“三个”同级,其前面有相同的第一个“二”同级。 我在 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>

【讨论】:

  • +1,务实的做法。空的&lt;ul&gt;&lt;/ul&gt; 不是很好,但也没有伤害。
  • @Tomalak 空的
      不太好 是的 - 当然这可以简单地解决:
    【解决方案3】:

    这种简单(没有内联 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(具有两个单独的定义),它通过其逻辑父元素的 generate-id() 值(分别为 one 或 @987654331)索引任何 twothree @)。这有助于我们拥有一个同时匹配 onetwo 元素的模板。

    2. 每个组内第一个(twothree)元素都在无模式下匹配和处理。在此模板中,会生成包装 ul,然后在名为 inGroup 的模式下处理组中的所有元素(作为参数传递)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-19
      • 1970-01-01
      • 1970-01-01
      • 2015-10-15
      相关资源
      最近更新 更多