如何在使用 xslt 2.0 保留内容的同时包装列表



>我有一个一直在玩的测试文件,我正在尝试包装元素,同时保持其余内容不变。我一直在尝试使用 for-for-pair,但它将每个列表项包装在一个

    中。如果我将模板匹配更改为根,我可以让它工作,但随后我失去了其他一切。有什么想法吗?提前谢谢。这是我到目前为止拥有的 xml 和 xsl。

XML 输入

<?xml version="1.0" encoding="UTF-8"?>
<document>
<chapter>
<title toc_id="new">An new story</title>
<para>New stories are different</para>
<listitem class="ol">Here is one list item</listitem>
<listitem class="ol">Here is two list item</listitem>
<listitem class="ol">Here is three list item</listitem>
<section>
<title toc_id="h2-intro-1">Introduction H1</title>
<para>Upon a time ....</para>
<title toc_id="h2-intro-2">Introduction H2</title>
<para>... there was a young prince. http://www.stuff.com, here's a website</para>
</section>
</chapter>
</document>

对于 xslt:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" 
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="listitem">
<xsl:for-each-group select="." group-by="@class">
<ol>
<xsl:for-each select=".">
<li>
<p>
<xsl:value-of select="."/>
</p>
</li>
</xsl:for-each>
</ol>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

我正在寻找的输出是这样的:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<document>
<chapter>
<title toc_id="new">An new story</title>
<para>New stories are different</para>
<ol>
<li>
<p>Here is one list item</p>
</li>
<li>
<p>Here is two list item</p>
</li>
<li>
<p>Here is three list item</p>
</li>
</ol>
<section>
<title toc_id="h2-intro-1">Introduction H1</title>
<para>Upon a time ....</para>
<title toc_id="h2-intro-2">Introduction H2</title>
<para>... there was a young prince. http://www.stuff.com, here's a website</para>
</section>
</chapter>
</document>

因为listitem的模板与每个项目匹配,并且您的fo-each-group仅适用于.(当前元素(,因此会导致包装每个元素。

您需要对选择进行更多调整。

您的模板只需要应用于listitem,其前身不是listitem(可能是具有相同类的列表项(。在模板内部,您需要打开ol标签,然后使用不同的规则来放置项目的内容和所有后续内容,只要它们是listitem

检查 XPath 轴:https://www.w3schools.com/xml/xpath_axes.asp

应该可以执行以下操作:

<xsl:template match="listitem[preceding-sibling::self[position()=1 and name() != 'listitem']]">
<ol>
<xsl:apply-templates mode="inlist" select="."/>
</ol>
</xsl:template>
<xsl:template match="listitem" mode="inlist">
<li><xsl:value-of select="."/></li>
<xsl:apply-templates  select="following-sibling::self[position() = 1 and name() = 'listitem']" mode="inlist"/>
</xsl:template>

我会使用match="*[listitem]"match="*[listitem[@class]]"来匹配包含要分组/包装的listitem元素的父元素,然后在父元素上使用for-each-group,如果您只希望它们是可以在 XSLT 3 中使用select="*" composite="yes" group-adjacent="boolean(self::listitem), @class"来标识具有相同class属性值的相邻listitem的元素:

<?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="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:template match="*[listitem]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="*" composite="yes" group-adjacent="boolean(self::listitem), @class">
<xsl:choose>
<xsl:when test="current-grouping-key()[1]">
<xsl:element name="{current-grouping-key()[2]}">
<xsl:apply-templates select="current-group()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="listitem">
<li>
<p>
<xsl:apply-templates/>
</p>
</li>
</xsl:template>
</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/gWmuiJK

使用 XSLT 2,您可以使用两个嵌套for-each-group

例如
<xsl:for-each-group select="*" group-adjacent="boolean(self::listitem)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:for-each-group select="current-group()" group-adjacent="@class">
<xsl:element name="{current-grouping-key()}">
<xsl:apply-templates select="current-group()"/>
</xsl:element>                          
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>

请参阅 https://xsltfiddle.liberty-development.net/gWmuiJK/1,对于完整的 XSLT 2 解决方案,您当然需要将xsl:mode声明替换为标识转换模板(已发布的代码中已有(。

最新更新