2009-05-21 12 views
9

Ho guardato a questa domanda: Select first instance only with XPath?Selezionare primo risultato in XPath

Ma se ho un nodo-set come questo:


<container value=""> 
    <data id="1"></data> 
    <data id="2">test</data> 
    <container> 
    <data id="1">test</data> 
    <data id="3">test</data> 
    </container> 
</container> 

Ora il mio scenario è che questo nodo-set è profondo all'interno di un documento, e ho un puntatore al contenitore interno. Quindi devo prefisso il mio XPath con "/ container/container" (il percorso è in realtà più lungo, ma per questo esempio dovrebbe farlo).

MODIFICA: Quello che voglio è un nodo "dati" con un ID di 1, che dovrebbe provenire dal nodo più basso prima o dall'antenato più vicino. Quindi, se non riesco a trovarlo sulla "corrente" (/ contenitore/contenitore) dovrei guardare gli antenati e prendere quello più vicino (o alla fine, non trovare nulla). Ho provato questo:

/contenitore/container/antenato-or-self :: contenitore/dati [@ id = "1"]

che riporta un set di risultati con due nodi. Pensavo di poter usare lo scorso() per ottenere quello più profondo, quindi l'ho attaccato alla fine, ma senza successo.

+0

La specifica potrebbe fare con maggiore chiarezza. Vuoi il nodo dati? Il nodo dati deve avere un id di 1? Crea il contenitore originale se il nodo dati non ha un ID 1 vuoi spostare il documento in un contenitore genitore per vedere se ha un nodo dati con un id di 1, se non continua fino al nodo dati con un id di 1 trovato. Questo lo descrive? – AnthonyWJones

+0

Ho cercato di descrivere quello che cercavo un po 'più chiaro, anche se alcuni lo hanno indovinato. Scusate! –

risposta

5

Come hai provato la virata last() su? Penso che questo dovrebbe funzionare:

/container/container/ancestor-or-self::container/data[@id="1"][last()] 

EDIT:

ragione, naturalmente, ho dimenticato le parentesi:

(/container/container/ancestor-or-self::container/data[@id="1"])[last()] 

che rende questo lo stesso come una delle altre risposte; Tuttavia, come sottolinea il manifesto originale, questa espressione non riesce su:

<container value=""> 
    <container> 
    <data id="1">a3</data> 
    <data id="3">a4</data> 
    </container> 
    <data id="1">a1</data> 
    <data id="2">a2</data> 
</container> 

Fedele allo spirito di StackOverflow, posso però combinare la mia risposta con una delle altre risposte e ottenere qualcosa che funziona in tutti i casi:

(/container/container/ancestor-or-self::container[data[@id="1"]])[last()]/data[@id="1"] 

A proposito, se ci sono più dati @ id-is-1 < figli di un contenitore, questo li restituirà tutti.Per restituire solo la prima di tali dati <> elemento:

(/container/container/ancestor-or-self::container[data[@id="1"]])[last()]/data[@id="1"][1] 
+0

Se la mia lettura qui sul risultato desiderato è corretta (grande se) questo non funzionerà perché l'ultimo() corrisponderà su entrambi i rami di antenato-o-sé - quello è il vero problema, quindi è necessario ritagliare il set di risultati sia in anticipo (come ho) o in ritardo (come Dave fa). – annakata

+0

Questo lo fa. Grazie mille (anche a tutti)! –

1

Io non sono al 100% chiaro su quello che stai chiedendo, ma se leggo questo diritto forse volete solo:

/container/container/ancestor-or-self::container[1]/data[@id='1'] 

nota "[1]"

Edit: pensato un po 'di più e il seguito funziona per me per tutti i casi finora alla ricerca di @ id = $ N

/container/container/ancestor-or-self::container[data[@id=$N]][1]/data[@id=$N] 

Piuttosto estrema xpathing proprio lì. In pratica, ciò che sta accadendo è che il nodeset di ancestor-self restituisce l'ordine più basso nella posizione 1 - quello è quello che vuoi - ma devi mettere una seconda condizione su quel nodo che ha anche un nodo di dati che ti interessa, una volta soddisfatte queste condizioni, hai il nodo giusto ora che vuoi solo scavare ulteriormente nei dati reali.

il 99% del tempo XPath diventa più facile se si prende a parte e pensa di esso come un algoritmo :)

+0

Ci ho provato, ma questo non copre tutti i casi. Cosa succede se sto cercando invece per @ id = '2'? Voglio favorire i risultati dal nodo corrente prima degli antenati, ma essere in grado di effettuare il fallback. –

+0

Vedi modifica: credo che copra tutti i casi elencati – annakata

0

Invece di

@id="1" 

hai provato:

position() == 1 
+0

Se position() == 1 funzionerebbe, rilasciare semplicemente ancestor-self :: tutti insieme. Penso che il requisito non riguardi solo un primo nodo di dati, ma un nodo di dati con un ID di 1. La domanda non è chiara come potrebbe essere. – AnthonyWJones

11

Assicurarsi che il last() è l'ambito in modo corretto. Prova:

(/container/container/ancestor-or-self::container/data[@id="1"])[last()] 

Allo stesso modo:

//container[last()] 

e:

(//container)[last()] 

non sono la stessa cosa. Il primo restituirà l'ultimo nodo contenitore ad ogni livello della gerarchia - più corrispondenze - e il secondo restituirà l'ultimo nodo contenitore trovato - una corrispondenza.

+0

Aha, questo ha funzionato! Grazie. –

+0

In realtà, ho scoperto che questo non funziona nel caso in cui il nodo contenitore figlio sia specificato prima di un elemento dati corrispondente. –

+0

@Nick Spacek - Ho migliorato la mia risposta qui sotto, e ora copre anche quella cassa d'angolo –

0

provare [position() = 1]

es.

/contenitore/contenitore/antenato-o-self :: contenitore/dati [position() = 1]

Problemi correlati