如何使用 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 的列表项具有以下兄弟项时
p/@class=ListContinue or p/@class=ListNote or p/@class=ListBullet2, 所有这些相邻的元素都应该被换行
- 除了 p/@class = BodyText 或
p/@class=注.
- 当第 2 级的列表项有以下兄弟项时
p/@class=ListContinue2 或 p/@class=ListNote,所有这些相邻的元素也应该包裹在
- 中,除非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>
我正在尝试对以下 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 的列表项具有以下兄弟项时 p/@class=ListContinue or p/@class=ListNote or p/@class=ListBullet2, 所有这些相邻的元素都应该被换行
- 除了 p/@class = BodyText 或 p/@class=注.
- 当第 2 级的列表项有以下兄弟项时 p/@class=ListContinue2 或 p/@class=ListNote,所有这些相邻的元素也应该包裹在
- 中,除非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>