如何使用 XSLT 分组将平面未分类列表分组为子列表

How to group a flat un-categorized list into sub lists using XSLT grouping

我正在尝试对以下 html 内容进行分组:

输入

<?xml version="1.0" encoding="UTF-8"?>
<html>
    <body>
        <h2>Steps for grouping in the Muench method</h2>
        <p class="step">Define a <code>key</code> for the property we want
            to use for grouping.</p>
        <p class="step">Select all of the nodes ...</p>
        <p class="substep">Select all of the nodes ...</p>
        <p class="result">Select all of the nodes ...</p>
        <p class="substep">Select all of the nodes ...</p>
        <p class="step">For each unique grouping value ...</p>
        <h2>Steps for grouping in XSLT 2.0</h2>
        <p class="step">Define an XPath expression ...</p>
        <p class="substep">Select all of the nodes ...</p>
        
        <p class="substep">Instead of dealing with each ...</p>
    </body>
</html>

进入如下输出

我在这里查看了几个答案,然后才将其发布到这里。他们给出了如何将目标元素与以下兄弟姐妹分组的想法。例如,当我想在子步骤中使用 @result 对元素进行分组时。以及步骤中的每个子步骤。

<body>
        <h2>Steps for grouping in the Muench method</h2>
        <step>
            <p class="step">Define a <code>key</code> for the property we want to use for
                grouping.</p>
        </step>
        <step>
            <p class="step">Select all of the nodes ...</p>
            <substep>
                <p class="substep">Select all of the nodes ...</p>
                <p class="result">Select all of the nodes ...</p>
            </substep>
            <substep>
                <p class="substep">Select all of the nodes ...</p>
            </substep>
        </step>
        <step>
            <p class="step">For each unique grouping value ...</p>
            <h2>Steps for grouping in XSLT 2.0</h2>
        </step>
        <step>
            <p class="step">Define an XPath expression ...</p>
            <substep>
                <p class="substep">Select all of the nodes ...</p>
            </substep>
            <substep>
                <p class="substep">Instead of dealing with each ...</p>
            </substep>
        </step>
    </body>

到目前为止我做了什么

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
    <xsl:key name="step" match="p" use="@class"/>
    <xsl:template match="/">
      <body>
        <xsl:variable name="steps">
            <xsl:for-each-group select="html/body/*" group-starting-with="p[@class = 'step']">
                <xsl:choose>
                    <xsl:when test="current-group()[self::p[@class = 'step']]">
                        <step>
                            <xsl:copy-of select="current-group()"/>
                        </step>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="."/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </xsl:variable>
        <xsl:apply-templates select="$steps" mode="fix.step"/>
      </body>
    </xsl:template>
    <xsl:template match="node()" mode="fix.step">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="step" mode="fix.step">
        <step>
            <xsl:for-each-group select="*" group-starting-with="p[@class = 'substep']">
                <xsl:choose>
                    <xsl:when test="current-group()[self::p[@class = 'substep']]">
                        <substep>
                            <xsl:copy-of select="current-group()"/>
                        </substep>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="current-group()"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </step>
    </xsl:template>
</xsl:stylesheet>

问题

有没有更好的达到同样的效果?

一些注意事项

给定的输入文件只是一个例子。 @step 和@substep 元素之间可能有很多非@step 和@substep 元素。并且它们需要根据它们的位置在步骤或子步骤中进行分组。例如,以下可能是输入文件的另一种变体:

<?xml version="1.0" encoding="UTF-8"?>
<html>
    <body>
        <h2>Steps for grouping in the Muench method</h2>
        <p class="step">Define a <code>key</code> for the property we want
            to use for grouping.</p>
        <p class="step">Select all of the nodes ...</p>
        <p class="result">Select all of the nodes ...</p>
        <image @href="image.pn"/>
        <p class="substep">Select all of the nodes ...</p>
        <p class="substep">Select all of the nodes ...</p>
        <p class="result">Select all of the nodes ...</p>
        <p class="substep">Select all of the nodes ...</p>
        <p class="step">For each unique grouping value ...</p>
        <h2>Steps for grouping in XSLT 2.0</h2>
        <p class="step">Define an XPath expression ...</p>
        <p class="substep">Select all of the nodes ...</p>
        
        <p class="substep">Instead of dealing with each ...</p>
    </body>
</html>

规则

  1. 当级别 1 的列表项具有以下兄弟项时 p/@class=ListContinue or p/@class=ListNote or p/@class=ListBullet2, 所有这些相邻的元素都应该被换行
  2. 除了 p/@class = BodyText 或 p/@class=注.
  3. 当第 2 级的列表项有以下兄弟项时 p/@class=ListContinue2 或 p/@class=ListNote,所有这些相邻的元素也应该包裹在
  4. 中,除非p/@class = BodyText 或 p/@class=Note 或 p/@class=ListBullet.

这样,与特定列表项相关的所有信息都包含在 li 中。无论深度如何,相同的逻辑适用于所有 li。就是我觉得很难做到。

你不能简单地做:

XSLT 2.0

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

<xsl:template match="/html">
    <body>
        <xsl:for-each-group select="body/*" group-starting-with="p[@class='step']">
            <xsl:choose>
                <xsl:when test="self::p[@class='step']">
                    <step>
                        <xsl:copy-of select="."/>     
                        <xsl:for-each-group select="current-group() except ." group-starting-with="p[@class='substep']">
                            <xsl:choose>
                                <xsl:when test="self::p[@class='substep']">
                                    <substep>
                                        <xsl:copy-of select="current-group()"/>
                                    </substep> 
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:copy-of select="current-group()"/>
                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:for-each-group>    
                    </step>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy-of select="."/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </body>
</xsl:template>

</xsl:stylesheet>