XSL替换单个元素命名空间URI并保留前缀的实现问题
问题描述
原XML文档:
<?xml version='1.0' encoding='utf-8'?> <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> <env:Body> <wd:Unpost_Job_Request xmlns:wd="urn:com.test.report" wd:version="v40.2"> <wd:Business_Process_Parameters> <wd:Auto_Complete>true</wd:Auto_Complete> <wd:Run_Now>true</wd:Run_Now> <wd:Discard_On_Exit_Validation_Error>true</wd:Discard_On_Exit_Validation_Error> <wd:Comment_Data> <wd:Comment>Unposting Triggered by Automation</wd:Comment> </wd:Comment_Data> </wd:Business_Process_Parameters> <wd:Unpost_Job_Data> <wd:Job_Posting_Reference> <wd:ID wd:type="Job_Posting_ID">JOB_POSTING-3-23427</wd:ID> </wd:Job_Posting_Reference> </wd:Unpost_Job_Data> </wd:Unpost_Job_Request> </env:Body> </env:Envelope>
需求:完整复制整个文档,仅将<wd:Unpost_Job_Request>元素及其所有子元素、属性的命名空间从urn:com.test.report替换为urn:com.final.report,预期输出如下:
<?xml version='1.0' encoding='utf-8'?> <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> <env:Body> <wd:Unpost_Job_Request xmlns:wd="urn:com.final.report" wd:version="v40.2"> <wd:Business_Process_Parameters> <wd:Auto_Complete>true</wd:Auto_Complete> <wd:Run_Now>true</wd:Run_Now> <wd:Discard_On_Exit_Validation_Error>true</wd:Discard_On_Exit_Validation_Error> <wd:Comment_Data> <wd:Comment>Unposting Triggered by Automation</wd:Comment> </wd:Comment_Data> </wd:Business_Process_Parameters> <wd:Unpost_Job_Data> <wd:Job_Posting_Reference> <wd:ID wd:type="Job_Posting_ID">JOB_POSTING-3-23427</wd:ID> </wd:Job_Posting_Reference> </wd:Unpost_Job_Data> </wd:Unpost_Job_Request> </env:Body> </env:Envelope>
原使用的XSL:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wd="urn:com.test.report" exclude-result-prefixes="xs" version="2.0"> <xsl:output indent="yes" method="xml" encoding="utf-8" omit-xml-declaration="yes"/> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="wd:Unpost_Job_Request"> <xsl:element name="{local-name(.)}"> <xsl:namespace name="wd">urn:com.final.report</xsl:namespace> <xsl:apply-templates select="node() | @*"/> </xsl:element> </xsl:template> </xsl:stylesheet>
问题:转换后未替换原有命名空间,反而新增命名空间,旧命名空间被添加_1后缀,子元素仍绑定旧命名空间,无法达到预期效果。
正确XSL实现方案
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:oldwd="urn:com.test.report" xmlns:wd="urn:com.final.report" exclude-result-prefixes="oldwd" version="2.0"> <xsl:output indent="yes" method="xml" encoding="utf-8" omit-xml-declaration="no"/> <!-- 基础复制模板:复制未匹配特殊规则的节点和属性 --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- 替换根目标元素的命名空间 --> <xsl:template match="oldwd:Unpost_Job_Request"> <wd:Unpost_Job_Request> <xsl:apply-templates select="@*"> <xsl:with-param name="new-ns" select="'urn:com.final.report'"/> </xsl:apply-templates> <xsl:apply-templates select="node()"> <xsl:with-param name="new-ns" select="'urn:com.final.report'"/> </xsl:apply-templates> </wd:Unpost_Job_Request> </xsl:template> <!-- 替换所有旧命名空间下的子元素 --> <xsl:template match="oldwd:*"> <xsl:param name="new-ns"/> <xsl:element name="wd:{local-name()}" namespace="{$new-ns}"> <xsl:apply-templates select="@*"> <xsl:with-param name="new-ns" select="$new-ns"/> </xsl:apply-templates> <xsl:apply-templates select="node()"> <xsl:with-param name="new-ns" select="$new-ns"/> </xsl:apply-templates> </xsl:element> </xsl:template> <!-- 替换所有旧命名空间下的属性 --> <xsl:template match="@oldwd:*"> <xsl:param name="new-ns"/> <xsl:attribute name="wd:{local-name()}" namespace="{$new-ns}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:template> </xsl:stylesheet>
方案说明
- 命名空间区分:将原
wd命名空间重命名为oldwd,新命名空间直接绑定为wd,避免命名冲突导致的前缀自动添加后缀问题。 - 全范围替换:不仅替换目标元素本身,还递归替换其所有子元素和属性的命名空间,确保整个分支都切换到新命名空间。
- 输出配置:将
omit-xml-declaration设为no,保证输出包含XML声明,与预期格式完全一致。
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

