xml - XSL 从平面树问题创建嵌套列表

标签 xml xslt xpath xslt-grouping

我需要能够从平面树创建嵌套列表。例如,输入可能是这样的:

<root>
    <h1>text</h1>
    <list level="1">num1</list>
    <list level="1">num2</list>
    <list level="2">sub-num1</list>
    <list level="2">sub-num2</list>
    <list level="3">sub-sub-num1</list>
    <list level="1">num3</list>
    <p>text</p>
    <list>num1</list>
    <list>num2</list>
    <h2>text</h2>
</root>

并且输出应该嵌套如下:

<root>
<h1>text</h1>
    <ol>
        <li>num1</li>
        <li>num2
             <ol>
                <li>sub-num1</li>
                <li>sub-num2
                    <ol>
                        <li>sub-sub-num1</li>
                    </ol>
                </li>
            </ol>
        </li>
        <li>num3</li>
    </ol>
    <p>text</p>
    <ol>
        <li>num1</li>
        <li>num2</li>
    </ol>
    <h2>text</h2>
</root>

我尝试了几种方法,但似乎无法理解。任何帮助是极大的赞赏。 注意:我需要使用 XSLT 1.0 执行此操作。

最佳答案

它差点让我发疯,但我完成了它。我花了将近 2 个小时。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="node() | @*">
    <xsl:copy>
        <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="list[not(preceding-sibling::*[1][self::list])]">
    <ol>
        <xsl:variable name="selfId" select="generate-id()"/>
        <xsl:call-template name="recurseItems"/>
        <xsl:apply-templates select="
            following-sibling::list
            [@level = 1 or not(@level)]
            [preceding-sibling::*[1][self::list]]
            [$selfId = generate-id(
                preceding-sibling::list[not(preceding-sibling::*[1][self::list])][1]
                )
            ]
            [not(position() = 1)]
            " mode="recurse"/>
    </ol>
</xsl:template>

<xsl:template name="recurseItems">
    <xsl:param name="nodes" select="."/>
    <xsl:variable name="nextStep" select="$nodes/following-sibling::*[1][self::list]"/>
    <xsl:choose>
        <xsl:when test="$nodes/@level and ($nodes/@level &lt; $nextStep/@level)">
            <li>
                <xsl:value-of select="$nodes"/>
                <ol>
                    <xsl:call-template name="recurseItems">
                        <xsl:with-param name="nodes" select="$nextStep"/>
                    </xsl:call-template>
                </ol>
            </li>
        </xsl:when>
        <xsl:when test="$nodes/@level and ($nodes/@level > $nextStep/@level)">
            <xsl:apply-templates select="$nodes" mode="create"/>
        </xsl:when>
        <xsl:when test="$nextStep">
            <xsl:apply-templates select="$nodes" mode="create"/>
            <xsl:call-template name="recurseItems">
                <xsl:with-param name="nodes" select="$nextStep"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="not($nextStep)">
            <xsl:apply-templates select="$nodes" mode="create"/>
        </xsl:when>
    </xsl:choose>
</xsl:template>

<xsl:template match="list" mode="recurse">
    <xsl:call-template name="recurseItems"/>
</xsl:template>

<xsl:template match="list" mode="create">
    <li>
        <xsl:value-of select="."/>
    </li>
</xsl:template>

<xsl:template match="list"/>

</xsl:stylesheet>

应用于稍微复杂一点的文档:

<root>
    <h1>text</h1>
    <list level="1">1.1</list>
    <list level="1">1.2</list>
    <list level="2">1.2.1</list>
    <list level="2">1.2.2</list>
    <list level="3">1.2.2.1</list>
    <list level="1">1.3</list>
    <p>text</p>
    <list>2.1</list>
    <list>2.2</list>
    <h2>text</h2>
    <h1>text</h1>
    <list level="1">3.1</list>
    <list level="1">3.2</list>
    <list level="2">3.2.1</list>
    <list level="2">3.2.2</list>
    <list level="3">3.2.2.1</list>
    <list level="1">3.3</list>
    <list level="2">3.3.1</list>
    <list level="2">3.3.2</list>
    <p>text</p>
</root>

它产生这个结果:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <h1>text</h1>
    <ol>
        <li>1.1</li>
        <li>1.2
            <ol>
                <li>1.2.1</li>
                <li>1.2.2
                    <ol>
                        <li>1.2.2.1</li>
                    </ol>
                </li>
            </ol>
        </li>
        <li>1.3</li>
    </ol>
    <p>text</p>
    <ol>
        <li>2.1</li>
        <li>2.2</li>
    </ol>
    <h2>text</h2>
    <h1>text</h1>
    <ol>
        <li>3.1</li>
        <li>3.2
            <ol>
                <li>3.2.1</li>
                <li>3.2.2
                    <ol>
                        <li>3.2.2.1</li>
                    </ol>
                </li>
            </ol>
        </li>
        <li>3.3
            <ol>
                <li>3.3.1</li>
                <li>3.3.2</li>
            </ol>
        </li>
    </ol>
    <p>text</p>
</root>

应用于您的示例,它也会产生正确的结果:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <h1>text</h1>
    <ol>
        <li>num1</li>
        <li>num2
            <ol>
                <li>sub-num1</li>
                <li>sub-num2
                    <ol>
                        <li>sub-sub-num1</li>
                    </ol>
                </li>
            </ol>
        </li>
        <li>num3</li>
    </ol>
    <p>text</p>
    <ol>
        <li>num1</li>
        <li>num2</li>
    </ol>
    <h2>text</h2>
</root>

关于xml - XSL 从平面树问题创建嵌套列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4651257/

相关文章:

c# - 从 linq to xml 中只选择一个随机节点

c# - 使用 Linq [C#] 加载重复命名的 XML 节点

java - 使用 Java 在文档中的任何位置定位 XML 元素

python - Selenium for Python : Get text() of node that is shared with another element, 通过 XPath

c# - 如何从 wcf 服务发送大型 xml 文件

xml - 如何在 XSLT 中获取以下同级

date - XSLT 1.0 比较日期

sql-server - SQL XPath 查询按属性查找节点值

xml - 在 Perl 中忽略 'Unclosed Token'

XML 到 XSLT 模式不匹配