2010-04-10 11 views
7

dato questo xml:XSLT e XPath: corrispondenza di precedenti commenti

<root> 
    <list> 
     <!-- foo's comment --> 
     <item name="foo" /> 
     <item name="bar" /> 
     <!-- another foo's comment --> 
     <item name="another foo" /> 
    </list> 
</root> 

mi piacerebbe utilizzare un XPath per selezionare tutte le voci-nodi che hanno un commento immediatamente precedente, cioè mi piace per selezionare il elementi "foo" e "altro foo", ma non la voce "bar".

Ho già manipolato l'asse fratello precedente e la funzione comment(), ma senza risultato.

+0

Si noti che nel modello di dati XSLT/XPath quegli elementi voce stai cercando un non preceduto immediatamente da un nodo di commento ma piuttosto da un nodo di testo con uno spazio bianco. Quindi, a meno che non si usi '' nei propri fogli di stile, questi nodi di testo dello spazio bianco possono interferire. –

+0

Buona domanda (+1). Vedi la mia risposta per l'unica (al momento) soluzione completa. I commenti di Martin Honnen sono corretti e sebbene la risposta attualmente selezionata possa funzionare per voi, potrebbe improvvisamente smettere di farlo (se il motore XPath viene fornito con un documento XML, i cui nodi solo nello spazio bianco non vengono rimossi). –

risposta

3

Questo sembra funzionare:

//comment()/following-sibling::*[1]/self::item 

Sembra per subito dopo i fratelli di commenti che sono anche <item> elementi. Non conosco un modo migliore per esprimere la parte ::*[1]/self::item, che è brutta; si noti che se fosse stato scritto ::item[1] allora sarebbe anche trovare <item> s non immediatamente proceduto da un commento.

+0

Cosa succede se c'è un'istruzione di elaborazione tra il commento e l'elemento 'elemento'? Certamente un caso limite, ma il manifesto originale deve prima chiarire se un elemento 'elemento' preceduto da un'istruzione di elaborazione preceduto da un commento è un elemento che desidera selezionare. –

+0

@ Kevin: Grazie, Kevin.Questo X-Path è abbastanza semplice da comprendere e funziona perfettamente :) @ Martin: Fortunatamente sono il maestro dell'input Xml, quindi sono certo che non ci saranno istruzioni di elaborazione. Grazie comunque per l'utile suggerimento. – miasbeck

+0

I commenti di Martin Honnen sono corretti. –

0

Come menzionato in this thread, introducendo un test (<xsl:if test="..."></xsl:if>) come:

preceding-sibling :: commento()

sarebbe solo test se il nodo contiene un fratello precedente che è commenti.

Se volete sapere, dei fratelli precedenti che sono elementi o commenti, se il più vicino è un commento, si potrebbe provare:

(preceding-sibling::*|preceding-sibling::comment())[1][self::comment()] # WRONG 

MA: che non funzionerà, perché se "[1]" significa prima nella direzione all'indietro per preceding-sibling, non significa che per un'espressione tra parentesi - è primi mezzi per documento

Si può provare:

(preceding-sibling::*|preceding-sibling::comment())[last()][self::comment()] 

o

preceding-sibling::node()[self::*|self::comment()][1][self::comment()] 

Per esempio:

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

    <xsl:template match="//item"> 
    <xsl:if test="preceding-sibling::node()[self::*|self::comment()][1][self::comment()]"> 
     <xsl:value-of select="./@name" /> 
    </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

sarebbe solo visualizzazione:

foo 
another foo 

quando si digita:

C:\Prog\xslt\preceding-sibling_comment> 
    java -cp ..\saxonhe9-2-0-6j\saxon9he.jar net.sf.saxon.Transform -s:test.xml -xsl:t.xslt -o:res.xml 

con:

  • test.xml: il file visualizzato nella tua domanda
  • t.xslt: il file XSLT sopra
  • res.xml: il file trasformato risultante

Modifica: poiché non tiene conto delle istruzioni di elaborazione, ho lasciato la risposta come Wiki della comunità.

+0

Grazie per la tua risposta completa VonC! Ho provato il costrutto "se" e funziona per me. Preferirei usare l'espressione di Kevin, dato che è qualcosa che posso capire (e, si spera, ricorda). – miasbeck

+0

Questo non funziona nella situazione descritta da Martin Honnen. –

+1

@Dimitre: +1 sulla tua risposta, e lascio il mio come Wiki di comunità. – VonC

3

La soluzione attualmente selezionato:

//comment()/following-sibling::*[1]/self::item 

non funziona nel caso in cui v'è un'istruzione procesing (o un intero gruppo di istruzioni di elaborazione) tra il commento e l'elemento - come notato in un commento di Martin Honnen.

La soluzione seguente non presenta un problema.

La seguente espressione XPath seleziona solo elementi nodi che sono o immediatamente preceduti da un nodo di commenti, o sono immediatamente preceduti da un nodo di testo bianco-spazio-only, che viene immediatamente preceduto da un nodo di commenti:

(//comment() 
    /following-sibling::node() 
    [1] 
    [self::text() 
    and 
    not(normalize-space()) 
    ] 
     /following-sibling::node() 
      [1] [self::item] 
) 
| 
(//comment() 
    /following-sibling::node() 
    [1] 
    [self::item] 
) 

Ecco un test completo:

usiamo questo documento XML:

<root> 
    <list> 
     <!-- foo's comment --> 
     <item name="foo" /> 
     <item name="bar" /> 
     <!-- another foo's comment --> 
     <item name="another foo" /> 
     <!-- comment 3 --><item name="immed.after 3"/> 
     <!-- comment 4 --><?PI ?><item name="after PI"/> 
    </list> 
</root> 

Quando la seguente trasformazione viene applicata sul documento sopra XML:

<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="/"> 
    <xsl:copy-of select= 
    " 
    (//comment() 
     /following-sibling::node() 
     [1] 
     [self::text() 
     and 
     not(normalize-space()) 
     ] 
      /following-sibling::node() 
       [1] [self::item] 
    ) 
    | 
    (//comment() 
     /following-sibling::node() 
     [1] 
     [self::item] 
    ) 
    "/> 
</xsl:template> 
</xsl:stylesheet> 

The Wanted, risultato corretto è prodotto:

<item name="foo"/> 
<item name="another foo"/> 
<item name="immed.after 3"/> 
+0

Risposta buona e completa. +1 – VonC

Problemi correlati