【问题标题】:Enhanced XSL to create nested list from flat tree problem增强的 XSL 从平面树问题中创建嵌套列表
【发布时间】:2011-03-18 14:07:59
【问题描述】:

不久前我问了一个非常相似的问题([1]:XSL to create nested list from flat tree problem“从平面树问题创建嵌套列表”),但我的问题稍微扩大了。最初我没有考虑嵌套在列表中的段落。所以现在,输入:

<root>
    <h1>text</h1>
    <list level="1">num1</list>
    <list level="1">num2</list>
    <para type="indent">indented para1</para>
    <list level="2">sub-num1</list>
    <para type="indent">sub-indented para1</para>
    <para type="indent">sub-indented para2</para>
    <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
            <paragraph>indented para1</paragraph>
            <ol>
                <li>sub-num1
                    <paragraph>sub-indented para1</paragraph>
                    <paragraph>sub-indented para2</paragraph>
                </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,再次感谢任何帮助。

【问题讨论】:

    标签: xml xslt xpath xslt-grouping


    【解决方案1】:

    没有过多修改我之前的answer,这个样式表:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:key name="kListByParent"
                 match="list"
                 use="concat(generate-id(
                                preceding-sibling::*[
                                   not(self::list|self::para)
                                ][1]
                             ), '+',
                             generate-id(
                                preceding-sibling::list[
                                   current()/@level > @level
                                ][1]
                             )
                      )"/>
        <xsl:key name="kParaByList" match="para"
                 use="generate-id(preceding-sibling::list[1])"/>
        <xsl:template match="node()|@*" name="identity">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="node()|@*" mode="copy">
            <xsl:call-template name="identity"/>
        </xsl:template>
        <xsl:template match="list[preceding-sibling::*[1][
                                     self::list|self::para
                                  ]
                             ] |
                             para"/>
        <xsl:template match="list">
            <xsl:variable name="vListMark"
                          select="generate-id(preceding-sibling::*[1])"/>
            <ol>
                <xsl:apply-templates select="key('kListByParent',
                                                 concat($vListMark,'+'))"
                                     mode="makeLi">
                    <xsl:with-param name="pListMark" select="$vListMark"/>
                </xsl:apply-templates>
            </ol>
        </xsl:template>
        <xsl:template match="list" mode="makeLi">
            <xsl:param name="pListMark"/>
            <xsl:variable name="vSubList"
                          select="key('kListByParent',
                                      concat($pListMark,'+',generate-id()))"/>
            <li>
                <xsl:apply-templates
                     select="node()|key('kParaByList',generate-id())"
                     mode="copy"/>
                <xsl:if test="$vSubList">
                    <ol>
                        <xsl:apply-templates select="$vSubList" mode="makeLi">
                            <xsl:with-param name="pListMark"
                                            select="$pListMark"/>
                        </xsl:apply-templates>
                    </ol>
                </xsl:if>
            </li>
        </xsl:template>
    </xsl:stylesheet>
    

    输出:

    <root>
        <h1>text</h1>
        <ol>
            <li>num1</li>
            <li>num2
                <para type="indent">indented para1</para>
                <ol>
                    <li>sub-num1
                        <para type="indent">sub-indented para1</para>
                        <para type="indent">sub-indented para2</para>
                    </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>
    

    注意:从概念上讲,这应该递归分组以list 开始,最小@level。我将对其进行重构以使其在语义上更加准确。

    编辑:在语义上更接近 XSLT 2.0 解决方案。

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="node()|@*">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="root">
            <xsl:copy>
                <xsl:for-each-group select="node()"
                     group-adjacent="boolean(self::list|self::para)">
                    <xsl:choose>
                        <xsl:when test="current-grouping-key()">
                            <xsl:call-template name="makeList">
                                <xsl:with-param name="pList"
                                                select="current-group()"/>
                            </xsl:call-template>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:apply-templates select="current-group()"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each-group>
            </xsl:copy>
        </xsl:template>
        <xsl:template name="makeList">
            <xsl:param name="pList"/>
            <xsl:variable name="vFirstList" select="($pList/self::list)[1]"/>
            <xsl:apply-templates
                 select="$pList[not(. >> $vFirstList)] except $vFirstList"/>
            <xsl:if test="$vFirstList">
                <ol>
                    <xsl:for-each-group select="$pList[not($vFirstList >> .)]"
                     group-starting-with="list[not($vFirstList/@level ne @level)]">
                        <li>
                            <xsl:value-of select="current-group()[1]"/>
                            <xsl:if test="current-group()[2]">
                                <xsl:call-template name="makeList">
                                    <xsl:with-param name="pList"
                                     select="current-group()[position()!=1]"/>
                                </xsl:call-template>
                            </xsl:if>
                        </li>
                    </xsl:for-each-group>
                </ol>
            </xsl:if>
        </xsl:template>
    </xsl:stylesheet>
    

    【讨论】:

    • @Jacqueline:不客气。请注意,XSLT 1.0 解决方案不像 XSLT 2.0 解决方案那样通用。我将再次访问此线程。
    猜你喜欢
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-09
    • 1970-01-01
    相关资源
    最近更新 更多