XSLT:平面 XML 到嵌套 HTML 列表

发布于 2024-11-11 16:47:20 字数 3318 浏览 0 评论 0原文

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

我会继续努力,但如果有人能给我一件救生衣,我将永远感激不已。谢谢!

I'm new to XSLT. I know I need to use xsl:for-each-group, but I can't figure out anything other than a basic list. Would some sort of recursion work better? Any XSLT 1.0 or 2.0 solution would be fine.

Below is the example XML. Note the most important attribute for organizing data into a tree structure is @taxonomy. Other attributes @taxonomyName and @level are provided as optional helper attributes.

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

Here's the expected output. (Please note that indenting is not necessary in the output. I've done it here for readability.)

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

Here's the best I can do.

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

I'll keep chugging away at it, but would be eternally grateful if someone could throw me a life jacket. Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

游魂 2024-11-18 16:47:20

好的,最后这是我的解决方案。 :-) 基本上它在树中递归,并且在每个级别,它都会执行 for-each-group group-by="the next level of @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>

我相信这就是您想要的。 (我将

放在那里,就像您在 XSL 尝试中所做的那样,用于类别名称而不是文档标题。)

OK, here's my solution at last. :-) Basically it recurses through the tree, and at each level, it does a for-each-group group-by="the next level of @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>

With the given input, the output is:

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

Which I believe is what you wanted. (I put <h3>s in there as you did in your XSL attempt, for the category names and not for the document titles.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文