XSL - How to remove unused namespaces from source xml?

岁酱吖の 提交于 2019-11-27 17:32:31

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!