提问人:EBamba 提问时间:11/1/2023 最后编辑:EBamba 更新时间:11/2/2023 访问量:45
如何使用 xslt 2.0 或 3.0 对相邻的 xml 元素进行分组?
How to group adjacent xml elements with xslt 2.0 or 3.0?
问:
我需要调整下面的 xslt 代码,以将与任何 p 相邻的元素(其名称属性包含“level_”作为子项)包装到前面的 p 中,后者在输出 xml 中转换为 li 标记。
下面是源 xml:
<root>
<header>Header text</header>
<p name="paragraph">Normal Paragraph</p>
<p name="level_1">Value 1</p>
<p name="level_1">Value 2 table</p>
<table frame="all" rowsep="1" colsep="1"
id="table_whs_jmk_hzb">
<title>table1</title>
<tgroup cols="2" align="center">
<colspec colname="c1" colnum="1" colwidth="1*"/>
<colspec colname="c2" colnum="2" colwidth="1*"/>
<thead>
<row>
<entry>Header 1</entry>
<entry>Header 2</entry>
</row>
</thead>
<tbody>
<row>
<entry>Testing cell 1 text</entry>
<entry>Testing cell 2</entry>
</row>
</tbody>
</tgroup>
</table>
<p name="level_2">Value 3</p>
<p name="level_2">Value 4</p>
<p name="level_1">Value 5 note</p>
<note>Another element</note>
<p name="level_1">Another Value 1</p>
<p name="level_2">Another Value 2</p>
<p name="level_2">Another Value 3 reference</p>
<reference>Some reference text</reference>
<p name="level_3">Another Value 4</p>
<p name="level_3">Another Value 5 example</p>
<example>Example text goes here</example>
<p name="level_4">Another Value 3 image</p>
<image src="./output/media/img(1).jpg"/>
<p name="level_4">Another Value 4</p>
<p name="level_1">Another Value 5</p>
<p name="level_1">Another Value 6</p>
</root>
所需的输出应如下所示:
<root>
<header>Header text</header>
<p name="paragraph">Normal Paragraph</p>
<ol outputclass="ol-level_1 ">
<li outputclass="li-level_1 ">Value 1</li>
<li outputclass="li-level_1 ">Value 2 table <table frame="all"
rowsep="1" colsep="1"
id="table_whs_jmk_hzb">
<title>table1</title>
<tgroup cols="2" align="center">
<colspec colname="c1" colnum="1" colwidth="1*"/>
<colspec colname="c2" colnum="2" colwidth="1*"/>
<thead>
<row>
<entry>Header 1</entry>
<entry>Header 2</entry>
</row>
</thead>
<tbody>
<row>
<entry>Testing cell 1 text</entry>
<entry>Testing cell 2</entry>
</row>
</tbody>
</tgroup>
</table>
<ol outputclass="ol-level-2 ">
<li outputclass="li-level_2 ">Value 3</li>
<li outputclass="li-level_2 ">Value 4</li>
</ol>
</li>
<li outputclass="li-level_1 ">Value 5 note <note>Another
element</note>
</li>
<li outputclass="li-level_1 ">Another Value 1
<ol outputclass="ol-level-2 ">
<li outputclass="li-level_2 ">Another Value 2</li>
<li outputclass="li-level_2 ">Another Value 3 reference
<reference>Some reference text</reference>
<ol outputclass="ol-level-3 ">
<li outputclass="li-level_3 ">Another Value 4</li>
<li outputclass="li-level_3 ">Another Value 5 example
<example>Example text goes here</example>
<ol outputclass="ol-level-4 ">
<li outputclass="li-level_4 ">Another Value 3 image
<image src="./output/media/img(1).jpg"/></li>
<li outputclass="li-level_4 ">Another Value 4</li>
</ol></li></ol></li></ol></li>
<li outputclass="li-level_1 ">Another Value 5</li>
<li outputclass="li-level_1 ">Another Value 6</li>
</ol></root>
这是 @martin-honnen 提供的 xslt,我想对其进行修改以获得所需的输出:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all" version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:function name="mf:group" as="node()*">
<xsl:param name="items" as="element()*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$items" group-starting-with="p[@name = 'level_' || $level]">
<xsl:choose>
<xsl:when test="self::p[@name = 'level_' || $level]">
<li outputclass="li-{@name}">
<xsl:apply-templates/>
<xsl:where-populated>
<ol outputclass="ol-level-{$level + 1}">
<xsl:sequence select="mf:group(tail(current-group()), $level + 1)"/>
</ol>
</xsl:where-populated>
</li>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="mf:group(current-group(), $level + 1)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="root">
<root>
<xsl:copy-of select="header"/>
<xsl:copy-of select="p[@name = 'paragraph']"/>
<ol outputclass="ol-level_1 ">
<xsl:sequence select="mf:group(p[@name = (1 to 6) ! ('level_' || .)], 1)"/>
</ol>
</root>
</xsl:template>
<!-- Templates for note, example, table, and image -->
<xsl:template match="note | example | table | image">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
答:
1赞
Martin Honnen
11/2/2023
#1
尝试将解决方案改编为上一个问题:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:function name="mf:group" as="node()*">
<xsl:param name="items" as="element()*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:variable name="first-level-item" select="$items[self::p[@name = 'level_' || $level]][1]"/>
<xsl:variable name="first-level-item-pos" select="index-of($items/generate-id(), generate-id($first-level-item))"/>
<xsl:apply-templates select="$items[empty($first-level-item-pos) or position() lt $first-level-item-pos]"/>
<xsl:if test="$first-level-item">
<ol outputclass="ol-{$first-level-item/@name}">
<xsl:for-each-group select="$items[position() ge $first-level-item-pos]" group-starting-with="p[@name = 'level_' || $level]">
<xsl:choose>
<xsl:when test="self::p[@name = 'level_' || $level]">
<li outputclass="li-{@name}">
<xsl:apply-templates/>
<xsl:sequence select="mf:group(tail(current-group()), $level + 1)"/>
</li>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="mf:group(current-group(), $level + 1)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</ol>
</xsl:if>
</xsl:function>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="root">
<xsl:copy>
<xsl:sequence select="mf:group(*, 1)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
评论
0赞
EBamba
11/2/2023
非常感谢@martin-honnen。代码按预期工作。
评论