Как сгруппировать плоский некатегоризированный список в подсписки с помощью группировки XSLT

Я пытаюсь сгруппировать следующий 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. When a list item at level 1 has following siblings p/@class=ListContinue or p/@class=ListNote or p/@class=ListBullet2, all such adjacent elements should be wrapped in
  2. as well except where the p/@class = BodyText or p/@class=Note.
  3. When a list item at level 2 has following siblings p/@class=ListContinue2 or p/@class=ListNote, all such adjacent elements should be wrapped in
  4. as well except where the p/@class = BodyText or p/@class=Note or p/@class=ListBullet.

Таким образом, вся информация, относящаяся к конкретному элементу списка, помещается в li. Та же логика применима ко всем li независимо от глубины. Это то, что мне трудно достичь.


person Antony    schedule 20.07.2020    source источник
comment
Я не вижу здесь ни мюнхской группировки, ни потребности в мюнхской группировке. Вы определили ключ, но не используете его.   -  person michael.hor257k    schedule 20.07.2020


Ответы (1)


Не могли бы вы сделать просто:

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>
person michael.hor257k    schedule 20.07.2020
comment
Спасибо @michael. Я все еще пытаюсь понять, как работает группировка в XSLT. Я искал решение, подобное тому, которое вы опубликовали. Я проверил ваш код на втором примере в вопросе. Выходные данные не включают тег изображения в пределах шага. - person Antony; 20.07.2020
comment
@Antony Трудно работать с примерами вместо правил. Я внес коррективы в свой ответ, но он предназначался только в качестве отправной точки для вас, чтобы вы могли расширить его по мере необходимости. - person michael.hor257k; 20.07.2020
comment
Большое спасибо за помощь в упрощении решения. - person Antony; 21.07.2020
comment
извините за этот глупый вопрос. Но, похоже, мне нужно изменить свой подход. Что вы подразумеваете под работой по правилам? Не могли бы вы уточнить это, если не возражаете? Это может дать мне некоторое представление о том, как изменить мой мыслительный процесс. - person Antony; 21.07.2020
comment
Я имел в виду, что ваш вопрос дал пример ввода и вывода, но не разработал логику, которую необходимо применить к вводу, чтобы получить вывод. Позже вы добавили еще один пример ввода, но все равно оставили нам догадываться, какой должна быть логика. Моя догадка может сработать для вас или нет — я не знаю. - person michael.hor257k; 21.07.2020
comment
прости за это. когда элемент списка сгруппирован, все его соседние элементы также должны быть заключены в ‹li›, за исключением случаев, когда p/@class = BodyText или p/@class=Note. Таким образом, вся информация, относящаяся к конкретному элементу списка, помещается в li. Та же логика применима ко всем li независимо от глубины. Это то, что мне трудно достичь. - person Antony; 21.07.2020
comment
Я добавил правила. - person Antony; 21.07.2020
comment
Ну, я не собираюсь тратить на это больше времени. Я думаю, у вас достаточно, чтобы внести свои коррективы - если нет, задайте новый вопрос. - person michael.hor257k; 21.07.2020
comment
Все нормально. Спасибо за помощь. - person Antony; 21.07.2020