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.
Ответы
Ответ 1
В отличие от ответа @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>
Ответ 2
Хорошо, если вы используете
<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>
чем вы просили.
Ответ 3
Добавляя к ответу 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>