如何在XSL中正确移除全局变量中的空元素
XSL处理全局变量时丢失子节点仅保留文本的问题修复
问题原因
$b变量的无效包裹:你在$b中用<xsl:copy>包裹$a的处理,$a本身就是Data元素,这会生成一个无名父节点,导致后续模板处理逻辑混乱。- 模式不匹配:空元素移除的模板属于默认模式,而
trim模式的模板内部调用<xsl:apply-templates>时未指定模式,触发了XSL默认模板——默认模板会递归输出所有文本节点、忽略元素节点,最终导致子元素丢失,仅拼接文本内容。 - 根模板标签笔误:根模板中闭合标签写了
<IMFRoot>,和开头的<Root>不匹配,破坏XML结构合法性。
修正后的XSL代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:f="f:f" exclude-result-prefixes="f xs xsl"> <xsl:output indent="yes" omit-xml-declaration="yes"/> <xsl:strip-space elements="*"/> <xsl:variable name="vPop" as="element()*"> <!-- Variables to be generated --> </xsl:variable> <xsl:variable name="seq" select="f:subTree($vPop/@path/concat(.,'/',string(..)))"/> <xsl:variable name="a"> <xsl:apply-templates select="$seq" mode="format"/> </xsl:variable> <!-- 修正$b的处理:直接对$a的节点应用trim模式,无需额外包裹 --> <xsl:variable name="b"> <xsl:apply-templates select="$a/node()" mode="trim"/> </xsl:variable> <!-- trim模式的身份模板:复制非空节点 --> <xsl:template match="node()|@*" mode="trim"> <xsl:copy> <xsl:apply-templates select="node()|@*" mode="trim"/> </xsl:copy> </xsl:template> <!-- trim模式下移除空元素:匹配没有属性、子元素、注释、处理指令且文本为空的元素 --> <xsl:template match="*[not(@*|*|comment()|processing-instruction()) and normalize-space()='']" mode="trim"/> <xsl:template match="Data" mode="format"> <Data> <!-- Some xsl:for-each and xsl:copy-of here --> </Data> </xsl:template> <!-- 修正根模板的闭合标签笔误 --> <xsl:template match="/"> <xsl:for-each select="Root"> <Root> <xsl:for-each select="Info"> <Info> <xsl:copy-of select="./*"></xsl:copy-of> </Info> </xsl:for-each> <xsl:copy-of select="$b"/> </Root> </xsl:for-each> </xsl:template> <xsl:function name="f:subTree" as="node()*"> <xsl:param name="pPaths" as="xs:string*"/> <xsl:for-each-group select="$pPaths" group-adjacent= "substring-before(substring-after(concat(., '/'), '/'), '/')"> <xsl:if test="current-grouping-key()"> <xsl:choose> <xsl:when test= "substring-after(current-group()[1], current-grouping-key())"> <xsl:element name= "{substring-before(concat(current-grouping-key(), '['), '[')}"> <xsl:sequence select= "f:subTree(for $s in current-group() return concat('/',substring-after(substring($s, 2),'/')) ) "/> </xsl:element> </xsl:when> <xsl:otherwise> <xsl:value-of select="current-grouping-key()"/> </xsl:otherwise> </xsl:choose> </xsl:if> </xsl:for-each-group> </xsl:function> </xsl:stylesheet>
关键修改点
$b变量优化:移除多余的<xsl:copy>,直接对$a的子节点应用trim模式,确保处理对象是Data元素本身。trim模式对齐:将空元素移除的模板指定为trim模式,同时在身份模板内部调用时明确指定mode="trim",保证递归处理时始终遵循trim规则,避免触发默认模板。- 标签错误修正:将根模板中的
<IMFRoot>改为<Root>,保证XML结构合法。
验证效果
处理你提供的输入后,会生成符合预期的结果:
<Root> <Info> <Name>Stack</Name> </Info> <Data> <Mode>Auto</Mode> <Class> <Details> <Version>1.0.0</Version> </Details> <Package>com</Package> </Class> </Data> </Root>
内容的提问来源于stack exchange,提问作者LiamFu
相关产品推荐
相关产品推荐

