如何使用 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' 的值传递,它们位于不同的命名空间中,我们再次获得所需的正确结果。
我们甚至可以通过一些额外的努力,让代码作为参数传递一个要取消命名空间的元素名称列表,它会取消所有这些的命名空间。
我可以成功地从所有元素中删除命名空间前缀,但只想从特定元素中删除前缀
我想从 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' 的值传递,它们位于不同的命名空间中,我们再次获得所需的正确结果。
我们甚至可以通过一些额外的努力,让代码作为参数传递一个要取消命名空间的元素名称列表,它会取消所有这些的命名空间。