问题
I have an xml with a lot of unused namespaces, like this:
<?xml version="1.0" encoding="UTF-8"?>
<ns1:Envelope xmlns:ns1="http://www.a.com" xmlns:ns2="http://www.b.com" xmlns:ns3="http://www.c.com" xmlns:ns4="http://www.d.com">
<ns1:Body>
<ns2:a>
<ns2:b>data1</ns2:b>
<ns2:c>data2</ns2:c>
</ns2:a>
</ns1:Body>
</ns1:Envelope>
I would like to remove the unused namespaces without having to specify in the xslt which ones to remove/maintain. The result xml should be this:
<?xml version="1.0" encoding="UTF-8"?>
<ns1:Envelope xmlns:ns1="http://www.a.com" xmlns:ns2="http://www.b.com">
<ns1:Body>
<ns2:a>
<ns2:b>data1</ns2:b>
<ns2:c>data2</ns2:c>
</ns2:a>
</ns1:Body>
</ns1:Envelope>
I've googled a lot but haven't found a solution to this particular issue. Is there any?
Thanks.
PS: Not 100% sure but I think it should be for XSL 1.0.
回答1:
Unlike the answer of @Martin-Honnen, this solution produces exactly the desired result -- the necessary namespace nodes remain where they are and are not moved down.
Also, this solution correctly deals with attributes that are in a namespace:
<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:template match="node()|@*" priority="-2">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:variable name="vtheElem" select="."/>
<xsl:for-each select="namespace::*">
<xsl:variable name="vPrefix" select="name()"/>
<xsl:if test=
"$vtheElem/descendant::*
[(namespace-uri()=current()
and
substring-before(name(),':') = $vPrefix)
or
@*[substring-before(name(),':') = $vPrefix]
]
">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document (the provided XML document with an added namespaced attribute):
<ns1:Envelope xmlns:ns1="http://www.a.com" xmlns:ns2="http://www.b.com" xmlns:ns3="http://www.c.com" xmlns:ns4="http://www.d.com">
<ns1:Body ns2:x="1">
<ns2:a>
<ns2:b>data1</ns2:b>
<ns2:c>data2</ns2:c>
</ns2:a>
</ns1:Body>
</ns1:Envelope>
the desired, correct result is produced:
<ns1:Envelope xmlns:ns1="http://www.a.com" xmlns:ns2="http://www.b.com">
<ns1:Body ns2:x="1">
<ns2:a>
<ns2:b>data1</ns2:b>
<ns2:c>data2</ns2:c>
</ns2:a>
</ns1:Body>
</ns1:Envelope>
回答2:
Well if you use
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="@* | text() | comment() | processing-instruction()">
<xsl:copy/>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
then unused namespaces are removed but the result is more likely to look like
<ns1:Envelope xmlns:ns1="http://www.a.com">
<ns1:Body>
<ns2:a xmlns:ns2="http://www.b.com">
<ns2:b>data1</ns2:b>
<ns2:c>data2</ns2:c>
</ns2:a>
</ns1:Body>
</ns1:Envelope>
than what you asked for.
回答3:
Adding to Dimitre's answer, if those namespaces should be preserved that only occur in attribute values, add this condition: @*[contains(.,concat($vPrefix,':'))]
:
<xsl:if test= "$vtheElem/descendant::* [namespace-uri() = current() and
substring-before(name(),':') = $vPrefix or
@*[substring-before(name(),':') = $vPrefix] or
@*[contains(.,concat($vPrefix,':'))]
]">
This will correctly preserve the namespace ns3
because of attrib="ns3:Header"
as in the following example.
<ns1:Envelope xmlns:ns1="http://www.a.com" xmlns:ns2="http://www.b.com" xmlns:ns3="http://www.c.com" xmlns:ns4="http://www.d.com">
<ns1:Body ns2:x="1">
<ns2:a>
<ns2:b atrib="ns3:Header">data1</ns2:b>
<ns2:c>data2</ns2:c>
</ns2:a>
</ns1:Body>
</ns1:Envelope>
来源:https://stackoverflow.com/questions/4593326/xsl-how-to-remove-unused-namespaces-from-source-xml