如何使用 XSLT 删除特定 xml 元素的命名空间前缀?

How to remove namespace prefix for specific xml element with XSLT?

我可以成功地从所有元素中删除命名空间前缀,但只想从特定元素中删除前缀

我想从 X509Data 元素中删除命名空间前缀:

<?xml version="1.0" encoding="UTF-8"?>
<xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
    </xenc:EncryptedKey>
    <ds:X509Data>
        <ds:X509Certificate>AAA=</ds:X509Certificate>
    </ds:X509Data>
</ds:KeyInfo>

至:

<?xml version="1.0" encoding="UTF-8"?>
<xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
    </xenc:EncryptedKey>
    <X509Data>
        <ds:X509Certificate>AAA=</ds:X509Certificate>
    </X509Data>
</ds:KeyInfo>

Java代码:

   public class TestXmlTransformer {

        public static void main(String[] args) throws SAXException, IOException, ParserConfigurationException,
                TransformerFactoryConfigurationError, TransformerException {

            InputStream xmlData = new FileInputStream("C:\Users\xxxxxx\Desktop\spoon\test1.xml");
            Document xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlData);

            Source stylesource = new StreamSource("C:\Users\xxxxxx\Desktop\spoon\test1.xsl");
            Transformer transformer = TransformerFactory.newInstance().newTransformer(stylesource);
            StringWriter stringWriter = new StringWriter();
            transformer.transform(new DOMSource(xmlDocument), new StreamResult(stringWriter));

            System.out.print(stringWriter.toString());
        }
    }

要仅从一个特定元素中删除命名空间,您必须使用自己的模板重新创建它(假设您在样式表中定义了命名空间 xmlns:ds="http://www.w3.org/2000/09/xmldsig#"):

<xsl:template match="ds:X509Data">
    <xsl:element name="X509Data">
        <xsl:apply-templates select="node()|@*" />
    </xsl:element>
</xsl:template>

输出与身份模板<xsl:mode on-no-match="shallow-copy"/>结合使用。

因此整个样式表可能如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <xsl:output method="xml" />

    <!-- Identity template --> 
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template> 

    <xsl:template match="ds:X509Data">
        <xsl:element name="X509Data">
            <xsl:apply-templates select="node()|@*" />
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

它的输出符合要求:

<?xml version="1.0" encoding="UTF-8"?>
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
  <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
  <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <xenc:EncryptedKey>
        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
    </xenc:EncryptedKey>
    <X509Data>
        <ds:X509Certificate>AAA=</ds:X509Certificate>
    </X509Data>
  </ds:KeyInfo>
</xenc:EncryptedData>

这是一个 XSLT 1.0 解决方案,其中要取消命名空间的元素名称事先未知,并作为参数传递:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:param name="pNameToUnNamespace" select="'X509Data'"/>

  <xsl:template match="node()|@*" name="identity">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*">
    <xsl:choose>
      <xsl:when test="local-name() = $pNameToUnNamespace">
        <xsl:element name="{local-name()}">
          <xsl:apply-templates select="node()|@*"/>
        </xsl:element>
      </xsl:when>
      <xsl:otherwise><xsl:call-template name="identity"/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

注意:这里我们不知道(也不需要)元素所属的命名空间。因此,我们已经实现了问题的更通用的解决方案。 除了 XSLT 命名空间外,转换中没有声明/使用其他命名空间!

当此转换应用于提供的 XML 文档时:

<xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
            <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
        </xenc:EncryptedKey>
        <ds:X509Data>
            <ds:X509Certificate>AAA=</ds:X509Certificate>
        </ds:X509Data>
    </ds:KeyInfo>
</xenc:EncryptedData>

产生了想要的、正确的结果:

<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
   <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
   <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <xenc:EncryptedKey>
         <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
      </xenc:EncryptedKey>
      <X509Data>
         <ds:X509Certificate>AAA=</ds:X509Certificate>
      </X509Data>
   </ds:KeyInfo>
</xenc:EncryptedData>

我们可以作为参数 'EncryptedKey' 或 'EncryptionMethod' 的值传递,它们位于不同的命名空间中,我们再次获得所需的正确结果。

我们甚至可以通过一些额外的努力,让代码作为参数传递一个要取消命名空间的元素名称列表,它会取消所有这些的命名空间。