XSL-как удалить неиспользуемые пространства имен из исходного xml?
у меня есть xml с большим количеством неиспользуемых пространств имен, например:
<?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>
Я хотел бы удалить неиспользуемые пространства имен без необходимости указывать в xslt, какие из них удалить / поддерживать. Результат xml должен быть таким:
<?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>
Я много гуглил, но не нашел решения этой конкретной проблемы. Есть такие?
спасибо.
PS: не 100% уверен, но я думаю, что это должно быть для XSL 1.0.
3 ответов
В отличие от ответа @Martin-Honnen, это решение дает именно желаемый результат -- необходимые узлы пространства имен остаются там, где они есть, и не перемещаются вниз.
кроме того, это решение правильно относится к атрибутам, которые находятся в пространстве имен:
<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>
когда это преобразование применяется к следующему XML-документу (предоставленный XML-документ с добавленным пространством имен атрибут):
<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>
получен желаемый, правильный результат:
<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>
Если вы используете
<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>
затем удаляются неиспользуемые пространства имен, но результат, скорее всего, будет выглядеть как
<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>
чем то, что вы просили.
добавление к ответу Dimitre, если эти пространства имен должны быть сохранены, которые происходят только в атрибуте значения добавьте следующее условие: @*[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,':'))]
]">
это правильно сохранит пространство имен ns3
из-за attrib="ns3:Header"
как в следующем примере.
<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>