2011-01-04 14 views
15

Ho un xml con un sacco di spazi dei nomi non utilizzati, in questo modo:XSL - Come rimuovere gli spazi dei nomi inutilizzati dall'origine 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> 

Vorrei rimuovere gli spazi dei nomi utilizzati senza dover specificare nel xslt quelli da rimuovere/mantenere. Il xml risultato dovrebbe essere questo:

<?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> 

Googled molto, ma non ho trovato una soluzione a questo problema particolare. C'è qualche?

Grazie.

PS: Non sicuro al 100% ma penso che dovrebbe essere per XSL 1.0.

+0

Nell'ambito dello spazio dei nomi URI che non fa parte di alcun QName non è lo stesso di non utilizzato. Si può pensare nelle definizioni dello schema, ad esempio –

risposta

18

Diversamente dalla risposta di @ Martin-Honnen, questa soluzione produce esattamente il risultato desiderato - i nodi dello spazio dei nomi necessari rimangono dove sono e non vengono spostati verso il basso.

Inoltre, questa soluzione si occupa correttamente con attributi che sono in uno spazio:

<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> 

quando questa trasformazione viene applicata sul seguente documento XML (il documento XML fornito con un attributo namespace aggiunto) :

<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> 

il, risultato corretto desiderato viene prodotto:

<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> 
+0

@mdiez: C'è un problema con gli spazi dei nomi ... Alcune implementazioni non gestiscono l'asso dei nomi dei nomi XPath. –

+0

Espandere il commento di Implementazioni che non conoscono l'asse dello spazio dei nomi: ciò comporterà la "spinta verso il basso" degli spazi dei nomi a ciascun elemento che utilizza lo spazio dei nomi. Questo, f.e., si applica al TransformerFactory predefinito di Java, mentre l'implementazione Saxon lo gestisce correttamente. – Leviathan

+0

Se si desidera conservare gli spazi dei nomi che si verificano solo nei valori dell'attributo * e quindi non sono necessari sintatticamente, consultare la risposta di @ Gentil di seguito. – Leviathan

3

Beh, se si utilizza

<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> 

namespace poi non utilizzati vengono rimossi, ma il risultato è più probabile che a guardare come

<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> 

di quello che hai chiesto.

+0

+1 Buona risposta senza l'asso "namespace'" non sempre implementato. Ma potrebbe finire con un sacco di dichiarazioni dello spazio dei nomi "duplicate" per gli elementi che sono i primi sotto l'URI dello spazio dei nomi per ogni ramo. –

+0

Risposta perfettamente buona. È semanticamente equivalente; la posizione delle dichiarazioni dello spazio dei nomi non ha importanza. –

1

aggiunta alla risposta di Dimitre, se quegli spazi devono essere conservati che si verificano solo in attributo valori, aggiungere questa condizione: @*[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,':'))] 
        ]"> 

Ciò correttamente preservare il namespace ns3 causa di attrib="ns3:Header" come nell'esempio seguente.

<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> 
Problemi correlati