XSLT 替换命名空间并添加新的(未使用的)命名空间

Posted

技术标签:

【中文标题】XSLT 替换命名空间并添加新的(未使用的)命名空间【英文标题】:XSLT to replace a namespace and also add a new (unused) namespace 【发布时间】:2021-05-17 20:11:31 【问题描述】:

我想替换以下 XML 文档的命名空间

<?xml version="1.0" encoding="UTF-8"?> 
<ns0:Document xmlns:ns0="http://mydata.com/H2H/Automation">    
  <CstmrCdtTrfInitn>
    <GrpHdr>
    </GrpHdr>
  </CstmrCdtTrfInitn> 
</ns0:Document>

以下

<?xml version="1.0" encoding="UTF-8"?> 
<Document xmlns="urn:iso:std:iso" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">    
  <CstmrCdtTrfInitn>
    <GrpHdr>
    </GrpHdr>
  </CstmrCdtTrfInitn> 
</Document>

关于可以转换它的 XSLT 有什么想法吗?

我尝试了以下 XSL,但它正在添加带有第二个节点的命名空间,并且也无法删除第一个命名空间。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output encoding="UTF-8" indent="yes"/>
  <xsl:template match="/*">
    <xsl:element name="local-name()" namespace="http://www.w3.org/2001/XMLSchema-instance">
      <xsl:copy-of select="./*" />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

【问题讨论】:

你到底在哪里坚持这个? 不知道如何编写 XSLT 来删除旧名称并添加两个新名称空间 【参考方案1】:

您的要求可能有点棘手:替换 Document 元素的默认命名空间很简单。但是在 XSLT-1.0 中添加未使用的 xslns:xsi 命名空间需要 EXSLT 扩展和 Michael Kay 在回复 this question 时解释的特殊技术。它涉及在全局变量中创建一个未使用的元素,然后将其命名空间复制到模板中以替换默认命名空间。在 XSLT-2.0 及更高版本中,这会更容易(见下文)。

EXSLT 扩展并非在所有 XSLT-1.0 处理器中都可用。但是有必要从变量中创建一个节点集。

所以所有的命名空间都要在xsl:stylesheet元素中定义,然后根元素(这里是ns0:Document)被一个模板匹配并替换为它的local-name()部分,添加了新的默认命名空间,然后是复制变量中定义的元素的“虚拟”命名空间。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://mydata.com/H2H/Automation" xmlns:urn="urn:iso:std:iso"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ext="http://exslt.org/common">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
    
    <!-- identity template (except elements)-->
    <xsl:template match="node()[not(self::*)]|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>  

    <xsl:variable name="nsXSI">
        <xsl:element name="xsi:dummy" namespace="http://www.w3.org/2001/XMLSchema-instance" />
    </xsl:variable>   
   
    <xsl:template match="ns0:*|*">
        <xsl:element name="local-name()" namespace="urn:iso:std:iso">
            <xsl:copy-of select="ext:node-set($nsXSI)/*/namespace::xsi" />
            <xsl:apply-templates select="node() | @*" />
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

即使在 XSLT-1.0 中,输出也应该符合预期:

<Document xmlns="urn:iso:std:iso" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <CstmrCdtTrfInitn>
      <GrpHdr>
      </GrpHdr>
   </CstmrCdtTrfInitn>
</Document>

简化的解决方案需要支持 XSLT-2.0 的处理器。然后你可以使用xsl:namespace指令如下,不需要“虚拟”变量:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://mydata.com/H2H/Automation">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>

    <!-- identity template (except elements)-->
    <xsl:template match="node()[not(self::element())]|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>  
   
    <xsl:template match="ns0:*|*">
        <xsl:element name="local-name(.)" namespace="urn:iso:std:iso">
            <xsl:namespace name="xsi">http://www.w3.org/2001/XMLSchema-instance</xsl:namespace>
            <xsl:apply-templates select="node() | @*" />
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

输出是一样的。


通过使用 XSLT-3.0+ 的 xsl:mode身份模板 替换为

,可以进一步简化上述 XSLT-2.0 解决方案
<xsl:mode on-no-match="shallow-copy"/>

【讨论】:

感谢您的快速回复。它给了我错误 sapxiapping:nsfinal.xsl: line 6: Error parsing XPath expression 'node()[not(self::element())]|@*' 你是对的。我必须添加一个名为 EXSLT 的外部库以使 XSLT-1.0 答案工作并将element() 更改为* 抱歉没有得到,我是否应该更改上述解决方案中的任何内容。 是的。我更改了 XSLT。您还需要 EXSLT 扩展来使用 XSLT-1.0 解决方案。如果你没有它,你会被 XSLT-2.0 或更高版本卡住。 感谢修改版本在一定程度上起作用,但它添加了 urn:iso:std:iso 与所有父节点 ns1 ns2 ns3【参考方案2】:

你有什么理由不能简单地做:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://mydata.com/H2H/Automation"
exclude-result-prefixes="ns0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="*">
    <xsl:element name="local-name()" namespace="urn:iso:std:iso">
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="/ns0:Document">
    <Document xmlns="urn:iso:std:iso" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  
        <xsl:apply-templates/>
    </Document>
</xsl:template>

</xsl:stylesheet>

【讨论】:

以上是关于XSLT 替换命名空间并添加新的(未使用的)命名空间的主要内容,如果未能解决你的问题,请参考以下文章

xsd:any 元素的命名空间前缀并使用 XSLT 添加命名空间前缀

单独的 xslt-replace 命名空间 uri

在可视化映射器中将命名空间添加到根节点

将命名空间从 java 传递给 xslt,并使用 java 中的参数作为 xslt 中的节点

XSLT:将命名空间添加到根元素

XSLT 将命名空间前缀添加到元素最佳实践