2012-02-21 12 views
13

Il seguente fa il lavoro di rimozione di elementi indesiderati e gli attributi per nome ("removeMe" in questo esempio) da un file XML:rimuovere gli elementi e/o attributi per nome per Parametri XSL

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="node() | @*" name="identity"> 
    <xsl:copy> 
    <xsl:apply-templates select="node() | @*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="removeMe"/> 
</xsl:stylesheet> 

I problemi sono non distingue tra elementi e attributi, il nome è hardcoded e può solo prendere un nome. Come è possibile riscriverlo per utilizzare un paio di parametri di input come quelli seguenti per rimuovere uno o più elementi e/o attributi specifici?

<xsl:param name="removeElementsNamed"/> 
<xsl:param name="removeAttributesNamed"/> 

Il risultato desiderato è la capacità di rimuovereuno o piùelementi e/ouno o piùattributi pur distinguendo tra elementi e attributi (in altre parole, dovrebbe essere possibile rimuovere tutti i "tempi" elementisenza eliminando anche tutti i "tempi" attributi).

Mentre ho richiesto XSLT 1.0 in questo round, le soluzioni XSLT 2.0 nelle risposte accettate e altre potrebbero essere utili per gli altri.

+0

Sei in grado di utilizzare XSLT 2.0? –

+0

@DevNull - Buona domanda. L'ho appena chiesto [qui] (http://stackoverflow.com/questions/9387396/upgrading-xslt-1-0-to-xslt-2-0). – Witman

+0

Grazie a tutto il buon input sulle risposte, la domanda è stata ampliata per chiarire la funzione desiderata, aggiungendo la funzionalità di rimozione degli attributi come una caratteristica distinta (da non mettere insieme alla rimozione degli elementi, ma disponibile nello stesso codice). – Witman

risposta

22

Questa trasformazione:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:param name="removeElementsNamed" select="'x'"/> 

<xsl:template match="node()|@*" name="identity"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="*"> 
    <xsl:if test="not(name() = $removeElementsNamed)"> 
    <xsl:call-template name="identity"/> 
    </xsl:if> 
</xsl:template> 
</xsl:stylesheet> 

quando applicato su qualsiasi documento XML, dicono che questo:

<t> 
    <a> 
     <b/> 
     <x/> 
    </a> 
    <c/> 
    <x/> 
    <d/> 
</t> 

produce il risultato corretto voluto - una copia del documento XML di origine in cui qualsiasi occorrenza di elemento avente il nome che è il valore del parametro $removeElementsNamed, viene cancellata:

<t> 
    <a> 
     <b/> 
    </a> 
    <c/> 
    <d/> 
</t> 

Do atto: In XSLT 1.0 it is syntactically illegal to have a variable or parameter reference inside a template match pattern. Questo è il motivo per cui le soluzioni di @Jan Thomä e @treeMonkey sollevano entrambi un errore con qualsiasi processore compatibile con XSLT 1.0.

Aggiornamento: Ecco una soluzione più complessa, che permette una lista tubo separato di nomi degli elementi - da cancellare, da passare alla trasformazione:

<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:param name="removeElementsNamed" select="'|x|c|'"/> 

<xsl:template match="node()|@*" name="identity"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="*"> 
    <xsl:if test= 
    "not(contains($removeElementsNamed, 
       concat('|',name(),'|') 
       ) 
     ) 
    "> 
    <xsl:call-template name="identity"/> 
    </xsl:if> 
</xsl:template> 
</xsl:stylesheet> 

Quando applicato alla stessa documento XML (sopra), la trasformazione produce di nuovo il ricercato, corretta in uscita - il documento XML di origine con tutti gli elementi il ​​cui nome sono specificati nel parametro $removeElementsNamed - cancellato:

<t> 
    <a> 
     <b/> 
    </a> 
    <d/> 
</t> 

Update2: la stessa trasformazione come nel Update1, ma scritto in XSLT 2.0:

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:param name="removeElementsNamed" select="'|x|c|'"/> 

<xsl:template match="node()|@*" name="identity"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match= 
"*[name() = tokenize($removeElementsNamed, '\|')]"/> 
</xsl:stylesheet> 

Aggiornamento: Il PO ha aggiunto l'obbligo di essere anche in grado di eliminare tutti gli attributi che hanno alcune specifiche nome.

Ecco la trasformazione leggermente modificato per accogliere questo nuovo requisito:

<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:param name="removeElementsNamed" select="'x'"/> 
    <xsl:param name="removeAttributesNamed" select="'n'"/> 

    <xsl:template match="node()|@*" name="identity"> 
     <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*"> 
     <xsl:if test="not(name() = $removeElementsNamed)"> 
     <xsl:call-template name="identity"/> 
     </xsl:if> 
    </xsl:template> 

    <xsl:template match="@*"> 
     <xsl:if test="not(name() = $removeAttributesNamed)"> 
     <xsl:call-template name="identity"/> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

Quando questa trasformazione è applicato sul documento XML sotto (quello usato prima, ma con pochi attributi aggiunti):

<t> 
    <a> 
     <b m="1" n="2"/> 
     <x/> 
    </a> 
    <c/> 
    <x/> 
    <d n="3"/> 
</t> 

The Wanted, risultato corretto è prodotto (tutti gli elementi denominati x e tutti gli attributi di nome n vengono cancellate):

<t> 
    <a> 
     <b m="1"/> 
    </a> 
    <c/> 
    <d/> 
</t> 

UPDATE2: come ancora una volta richiesto dal PO, ora implementare la capacità di passare lista tubi separati di nomi per la cancellazione di elementi con questi nomi e rispettivamente un elenco tubo separato di nomi per l'eliminazione di attributi con questi nomi:

<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:param name="removeElementsNamed" select="'|c|x|'"/> 
    <xsl:param name="removeAttributesNamed" select="'|n|p|'"/> 

    <xsl:template match="node()|@*" name="identity"> 
     <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*"> 
     <xsl:if test= 
     "not(contains($removeElementsNamed, 
        concat('|', name(), '|') 
        ) 
      ) 
     "> 
     <xsl:call-template name="identity"/> 
     </xsl:if> 
    </xsl:template> 

    <xsl:template match="@*"> 
     <xsl:if test= 
     "not(contains($removeAttributesNamed, 
        concat('|', name(), '|') 
        ) 
      ) 
     "> 
     <xsl:call-template name="identity"/> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

Quando questa trasformazione viene applicata sul seguente documento XML:

012.351.641,061 mila
<t> 
    <a p="0"> 
     <b m="1" n="2"/> 
     <x/> 
    </a> 
    <c/> 
    <x/> 
    <d n="3"/> 
</t> 

The Wanted, risultato corretto è prodotto (elementi con nomi c e x e gli attributi con i nomi n e p vengono cancellati):

<t> 
    <a> 
     <b m="1"/> 
    </a> 
    <d/> 
</t> 
+0

Come gestiresti più nomi di elementi passato nel param? (L'OP implica più nomi basati sull'uso plurale di "elementi" in '$ removeElementsNamed') –

+0

oi! min non nella partita patern! dovrebbe funzionare, tuttavia non posso testare a casa non ho alcun software di sviluppo atm: _ ( – Treemonkey

+0

@DevNull: Vuole dire: rimuovere tutti gli elementi denominati XXX.questo è il motivo per cui usa il plurale. Naturalmente, se l'OP chiarisce che deve eliminare elementi che hanno un nome da un elenco di nomi, Sarei felice di dare lui la soluzione appropriata. :) –

0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output omit-xml-declaration="yes" indent="yes"/> 
    <xsl:param name="removeMe"/> 

    <xsl:template match="node() | @*"> 
     <xsl:if test="not(name(.)=$removeMe)"> 
     <xsl:copy> 
      <xsl:apply-templates select="node() | @*"/> 
     </xsl:copy> 
     </xsl:if> 
    </xsl:template> 


</xsl:stylesheet> 
+1

?? '$ removeMe' non è definito da nessuna parte. –

+1

Questo non filtra solo un nome di un singolo elemento? –

+0

si lo è! è proprio lì! – Treemonkey

-1

Questo è somehwat hacky, ma potrebbe dare l'idea generale:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:param name="removeElementsNamed"/> 

<xsl:template match="node() | @*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node() | @*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="*[contains($removeElementsNamed, concat(',',name(),','))]"/> 

È necessario specificare i nomi degli elementi per rimuovere da un elenco separato da virgole, a partire da un virgola e termina con una virgola, ad es il valore ", foo, bar, baz," rimuoverà tutti gli elementi denominati foo bar o baz. Se non si dispone di elementi che sono nomi parziali di altri elementi è possibile semplificare questo per:

<xsl:template match="*[contains($removeElementsNamed,name())]"/> 

Tuttavia, se si dispone di un XML come

<foo> 
    <bar>..<bar> 
    <barbara>..</barbara> 
<foo> 

e l'uso "bar" come parametro, questo cancellerà sia la barra che i tag barbara, quindi il primo approccio è più sicuro.

+0

Come indicato da Dimitre, questo genera un errore. L'elaborazione con msxml3.dll si arresta con errore: "Le variabili non possono essere utilizzate all'interno di questa espressione." e si riferisce a match = "* [contiene ($ removeElementsnamed ... – Witman

+0

Vedo, questa soluzione non funziona con XSLT 1.0, quindi di nuovo questo non è stato specificato come requisito :) –

+0

Grazie per la risposta. la domanda non era più chiara all'inizio: non sapevo nulla di XSLT 2.0 fino a quando il DevNull non l'ha fatto ... imparando ogni giorno e sperando di usare XSLT 2.0 in futuro. – Witman

3

Ecco un'opzione XSLT 2.0 se si può usare 2.0 . I nomi degli elementi possono essere passati come virgola, tabulazione, pipe o spazio separato.

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:param name="removeElementsNamed" select="'bar,baz'"/> 

    <xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*[name()=tokenize($removeElementsNamed,'[\|, \t]')]"/> 

</xsl:stylesheet> 
+0

+1 per una buona risposta. –

+0

Mi fai desiderare XSLT 2.0. Forse la prossima volta ... Grazie! – Witman

Problemi correlati