如何用XSLT移除XML指定xmlns声明且保留带命名空间的元素
我来帮你搞定这个XSLT的问题!你的需求很明确:移除XML里所有的xmlns声明,但要保留带shipping前缀的元素和message:timestamp属性。不过当前的XSLT代码会把shipping:min、shipping:max的前缀给去掉,完全不符合预期,咱们来调整一下:
问题回顾
原始XML
<Orders xmlns="hi/hi" xmlns:shipping="hello/hi" xmlns:message="hello/hi" message:timestamp="2018-04-23T14:28:39Z"> <orderLineStatusUpdate orderNumber="CCORD002" line="0"> <orderLineStatus>inPicking</orderLineStatus> <shipmentNumber>1</shipmentNumber> <leadTime unit="days"> <shipping:min>1</shipping:min> <shipping:max>1</shipping:max> </leadTime> </orderLineStatusUpdate> </Orders>
期望输出
<Orders message:timestamp="2018-04-23T14:28:39Z"> <orderLineStatusUpdate orderNumber="CCORD002" line="0"> <orderLineStatus>inPicking</orderLineStatus> <shipmentNumber>1</shipmentNumber> <leadTime unit="days"> <shipping:min>1</shipping:min> <shipping:max>1</shipping:max> </leadTime> </orderLineStatusUpdate> </Orders>
当前错误的XSLT代码
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" /> <xsl:template match="*"> <xsl:element name="{local-name(.)}"> <xsl:apply-templates select="@* | node()" /> </xsl:element> </xsl:template> <xsl:template match="@*"> <xsl:attribute name="{local-name(.)}"> <xsl:value-of select="." /> </xsl:attribute> </xsl:template> <xsl:template match="@*[local-name(.)='message']" /> </xsl:stylesheet>
问题分析
当前代码的问题出在这几点:
- 处理元素时用了
local-name(.),会直接去掉所有元素的前缀,导致shipping:min变成min,完全丢失了前缀标识; - 处理属性时同样用了
local-name(.),会把message:timestamp的前缀去掉,变成普通的timestamp属性; - 最后一个模板匹配
@*[local-name(.)='message']完全是错误的,它试图删除名为message的属性,但我们要删除的是xmlns相关的声明,不是message前缀的属性。
正确的XSLT实现
我们需要调整模板逻辑,确保保留元素的前缀,同时只移除xmlns和xmlns:*属性,其他属性(包括带前缀的message:timestamp)全部保留:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/> <!-- 身份模板:复制文本、注释、处理指令等非元素/属性节点 --> <xsl:template match="node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- 复制元素:保留原名称(包括前缀) --> <xsl:template match="*"> <xsl:element name="{name()}"> <xsl:apply-templates select="@* | node()"/> </xsl:element> </xsl:template> <!-- 处理属性:排除xmlns和xmlns:*属性,其他保留原名称 --> <xsl:template match="@*"> <xsl:if test="not(starts-with(name(), 'xmlns'))"> <xsl:attribute name="{name()}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:if> </xsl:template> </xsl:stylesheet>
代码说明
- 身份模板:专门处理文本节点、注释、处理指令等非元素/属性节点,直接复制它们,避免内容丢失;
- 元素模板:用
name()代替local-name(.),这样会完整保留元素的前缀(比如shipping:min会原样保留); - 属性模板:通过
starts-with(name(), 'xmlns')判断是否是命名空间声明属性,是的话跳过移除,其他属性(包括message:timestamp)都保留原名称和值。
用这个代码处理后,就能得到你想要的结果:所有xmlns声明被移除,shipping前缀的元素和message:timestamp属性都完整保留。
内容的提问来源于stack exchange,提问作者Techgeeks1
相关产品推荐
相关产品推荐

