如何用XSLT 2.0/3.0分组相邻XML元素并调整嵌套结构
修改XSLT实现相邻元素包裹需求
需要调整现有XSLT代码,将所有与name属性包含level_的<p>元素相邻的非层级元素(如<table>、<note>、<reference>、<example>、<image>等),作为子元素包裹到前一个会转换为<li>标签的<p>元素中。
源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>
修改后的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}"> <!-- 输出当前<p>的文本内容 --> <xsl:apply-templates/> <!-- 输出当前组内非层级元素(如table、note、reference等) --> <xsl:apply-templates select="current-group()[not(self::p[@name matches 'level_\d+'])]"/> <!-- 递归处理下一层级的<p>元素 --> <xsl:variable name="next-level" select="$level + 1"/> <xsl:where-populated> <ol outputclass="ol-level-{$next-level}"> <xsl:sequence select="mf:group(current-group()[self::p[@name = 'level_' || $next-level]], $next-level)"/> </ol> </xsl:where-populated> </li> </xsl:when> <xsl:otherwise> <!-- 处理不在任何层级<p>开头的元素(理论上不会出现,除非输入有异常) --> <xsl:apply-templates select="current-group()"/> </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 "> <!-- 传入所有需要处理的元素:除header和普通段落外的所有元素 --> <xsl:sequence select="mf:group(* except (header, p[@name='paragraph']), 1)"/> </ol> </root> </xsl:template> <!-- 保留原有的元素复制模板,新增reference处理 --> <xsl:template match="note | example | table | image | reference"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>
关键修改说明
Root模板范围扩展:
原代码仅传入层级<p>元素,现在改为传入root下除<header>和普通段落外的所有元素,确保相邻非层级元素被纳入处理流程。分组后元素处理逻辑:
在mf:group函数中,生成<li>后,先输出当前<p>的内容,再直接将组内的非层级元素(如<table>、<note>)作为<li>的子元素输出,实现包裹需求。层级递归优化:
筛选当前组内的下一层级<p>元素,递归调用分组函数生成对应的<ol>列表,保持原有层级结构的正确性。元素模板扩展:
新增<reference>元素的复制模板,确保所有需要保留的元素都能正确输出。
内容的提问来源于stack exchange,提问作者EBamba
相关产品推荐
相关产品推荐

