XSLT 1.0身份模板使用异常:过滤XML节点时丢失字段
问题描述
使用XSLT 1.0处理大型XML响应,需要移除部分无用字段,要求用apply-templates而非for-each。现有代码虽能移除无用字段,但匹配范围外的其他字段也消失了,是否错误使用了身份模板?
输入XML
<?xml version="1.0" encoding="UTF-8"?> <xsl:object xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:array name="BankDetails"> <one>field1</one> <two>field2</two> <xsl:object> <date>1234-56-78</date> <Time>12345678</Time> </xsl:object> <xsl:object> <date>0000-00-00</date> <Time>00000000</Time> </xsl:object> <xsl:object> <date>0000-00-00</date> <Time>00000000</Time> </xsl:object> <xsl:object> <date>0000-00-00</date> <Time>00000000</Time> </xsl:object> <three>field3</three> <four>field4</four> <five>field5</five> <six>field6</six> </xsl:array> </xsl:object>
原XSLT代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias" xmlns:str="http://exslt.org/strings"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" /> <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl" /> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="xsl:object/xsl:array"> <xsl:for-each select="xsl:object[Time !='00000000']"> <xsl:copy-of select = "."/> </xsl:for-each> </xsl:template> </xsl:stylesheet>
实际输出
<?xml version="1.0" encoding="UTF-8"?><xsl:object xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:object> <date>1234-56-78</date> <Time>12345678</Time> </xsl:object> </xsl:object>
期望输出
<?xml version="1.0" encoding="UTF-8"?> <xsl:object xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:array name="BankDetails"> <one>field1</one> <two>field2</two> <xsl:object> <date>1234-56-78</date> <Time>12345678</Time> </xsl:object> <three>field3</three> <four>field4</four> <five>field5</five> <six>field6</six> </xsl:array> </xsl:object>
问题分析与解决方案
你的身份模板本身没有错误,问题出在自定义的xsl:object/xsl:array模板上:
- 这个模板直接覆盖了身份模板对
<xsl:array>节点的处理逻辑,只输出了符合条件的<xsl:object>,完全忽略了<xsl:array>本身的复制,以及数组内的<one>、<two>、<three>等其他子节点,导致这些内容全部丢失。
正确的做法是只针对需要移除的节点编写匹配模板,让身份模板处理其余所有节点,完全不需要for-each:
- 保留身份模板,确保所有需要保留的节点(包括
<xsl:array>、<one>、<two>等)都能被正常复制 - 新增一个模板,匹配那些需要移除的
<xsl:object>节点(即xsl:array/xsl:object[Time='00000000']),模板内容为空,这样这些节点就会被自动跳过
修正后的XSLT代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias" xmlns:str="http://exslt.org/strings"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" /> <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl" /> <!-- 身份模板:复制所有未被特殊模板匹配的节点和属性 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 匹配并移除Time为00000000的xsl:object节点 --> <xsl:template match="xsl:array/xsl:object[Time='00000000']" /> </xsl:stylesheet>
效果验证
使用上述代码处理输入XML,将得到你期望的输出:<xsl:array>节点被完整保留,其中的<one>、<two>、<three>等字段全部保留,只有Time='00000000'的<xsl:object>被移除。
内容的提问来源于stack exchange,提问作者Mano
相关产品推荐
相关产品推荐

