基于Xpath //book/part/chapter/sect拆分XML的XSLT实现求助
基于XPath拆分XML的XSLT解决方案
需求背景
需要将XML文件按//book/part/chapter/sect节点拆分,每个输出文件保留完整的<book>层级结构,仅包含单个<sect>节点;同时要求第一个输出文件保留<prelims>节点,其余输出文件移除该节点。
原始XML代码
<book> <prelims>This is prelims</prelims> <part>This is part <chapter>This is Chapter <level><sect>This is sect1</sect></level> <level><sect>This is sect2</sect></level> <level><sect>This is sect3</sect></level> <level><sect>This is sect4</sect></level> </chapter> </part> </book>
期望拆分输出
- 输出1(含prelims):
<book> <prelims>This is prelims</prelims> <part>This is part <chapter>This is Chapter <level><sect>This is sect1</sect></level> </chapter> </part> </book>
- 输出2:
<book> <part>This is part <chapter>This is Chapter <level><sect>This is sect2</sect></level> </chapter> </part> </book>
- 输出3:
<book> <part>This is part <chapter>This is Chapter <level><sect>This is sect3</sect></level> </chapter> </part> </book>
- 输出4:
<book> <part>This is part <chapter>This is Chapter <level><sect>This is sect4</sect></level> </chapter> </part> </book>
现有XSLT代码问题
当前代码仅复制了sect节点本身,未保留完整的<book>层级结构,也未处理<prelims>的显示逻辑,且xsl:for-each select="."属于冗余操作,无法实现预期效果:
<?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:output method="xml" version="1.0" encoding="UTF-8" indent="no" exclude-result-prefixes="#all"/> <xsl:template match="@*|node()"> <xsl:copy><xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="//sect"> <xsl:for-each select="."> <xsl:variable name="increment" select="position()"/> <xsl:result-document href="try{$increment}.xml"> <xsl:copy><xsl:apply-templates/></xsl:copy> </xsl:result-document> </xsl:for-each> </xsl:template> </xsl:stylesheet>
正确XSLT实现
以下是符合需求的XSLT 2.0代码:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" exclude-result-prefixes="#all"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <!-- 全局变量:获取所有目标sect节点 --> <xsl:variable name="all-sects" select="//book/part/chapter/level/sect"/> <!-- 从根节点book开始处理,遍历每个sect生成输出文件 --> <xsl:template match="/book"> <xsl:for-each select="$all-sects"> <xsl:variable name="current-sect" select="."/> <xsl:variable name="file-index" select="position()"/> <xsl:result-document href="try{$file-index}.xml"> <book> <!-- 仅第一个输出文件保留prelims --> <xsl:if test="$file-index = 1"> <xsl:copy-of select="../prelims"/> </xsl:if> <!-- 复制part节点,并传递目标sect参数 --> <xsl:apply-templates select="part"> <xsl:with-param name="target-sect" select="$current-sect" tunnel="yes"/> </xsl:apply-templates> </book> </xsl:result-document> </xsl:for-each> </xsl:template> <!-- 复制part节点,传递目标sect参数 --> <xsl:template match="part"> <xsl:param name="target-sect" tunnel="yes"/> <xsl:copy> <xsl:copy-of select="text()"/> <xsl:apply-templates select="chapter"> <xsl:with-param name="target-sect" select="$target-sect" tunnel="yes"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <!-- 复制chapter节点,传递目标sect参数 --> <xsl:template match="chapter"> <xsl:param name="target-sect" tunnel="yes"/> <xsl:copy> <xsl:copy-of select="text()"/> <!-- 仅保留包含目标sect的level节点 --> <xsl:apply-templates select="level[sect is $target-sect]"/> </xsl:copy> </xsl:template> <!-- 复制level和sect节点 --> <xsl:template match="level|sect"> <xsl:copy> <xsl:copy-of select="@*|text()"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> <!-- 忽略其他节点的默认复制(避免多余内容) --> <xsl:template match="text()" priority="-1"/> </xsl:stylesheet>
代码核心逻辑说明
- 全局变量定位:
all-sects变量获取所有需要拆分的sect节点,作为遍历生成文件的基础。 - 多文件生成:通过
xsl:result-document为每个sect生成独立XML文件,文件名按索引编号。 - prelims控制:仅第一个输出文件复制
prelims节点,其余文件自动忽略。 - 层级过滤:在
chapter节点处理时,仅保留包含当前目标sect的level节点,确保每个输出文件只有一个sect。 - 结构保留:通过模板匹配复制
part、chapter等上层节点及文本内容,完整保留原始XML的层级结构。
内容的提问来源于stack exchange,提问作者siva2012
相关产品推荐
相关产品推荐

