2010-01-29 26 views
34

He aquí un extracto de mi xml:XPath: seleccionar todos los siguientes hermanos hasta que otro hermano

<node/> 
<node/> 
<node id="1">content</node> 
<node/> 
<node/> 
<node/> 
<node id="2">content</node> 
<node/> 
<node/> 

estoy posicionado en el node[@id='1']. Necesito un Xpath para unir todos los elementos <node/> hasta el siguiente nodo no vacío (aquí node[@id='2']).


Editar: los atributos @Id son sólo para explicar mi problema con mayor claridad, pero no están en mi XML original. Necesito una solución que no use los atributos @id.


hago no desea hacer coincidir los hermanos vacías después de node[@id='2'], así que no puedo utilizar una ingenua following-sibling::node[text()=''].

¿Cómo puedo lograrlo?

+0

Ver también: http://stackoverflow.com/questions/2165566/xslt-select-following-sibling -toil-reach-a-specified-tag – WBT

+0

Esto podría ser útil: [http://stackoverflow.com/questions/2063619/how-to-reformat-xml-with-group-adjacent-xslt](http:/ /stackoverflow.com/questions/2063619/how-to-reformat-xml-with-group-adjacent-xslt) – igor

Respuesta

21

Usted podría hacerlo de esta manera:

 
../node[not(text()) and preceding-sibling::node[@id][1][@id='1']] 

donde '1' es el id del nodo actual (generan dinámicamente la expresión).

La expresión dice:

  • del contexto actual vaya a la matriz
  • seleccionar los nodos secundarios que
  • no tener ningún texto y
  • de todo "que precede nodos hermanos que tienen un identificador "el primero debe tener una identificación de 1

Si se encuentra en XSLT, puede seleccionar uno de los siguientes ejes hermanos bec ausa puede utilizar la función current():

<!-- the for-each is merely to switch the current node --> 
<xsl:for-each select="node[@id='1']"> 
    <xsl:copy-of select=" 
    following-sibling::node[ 
     not(text()) and 
     generate-id(preceding-sibling::node[@id][1]) 
     = 
     generate-id(current()) 
    ] 
    " /> 
</xsl:for-each> 

o más simple (y más eficiente) con una clave:

<xsl:key 
    name="kNode" 
    match="node[not(text())]" 
    use="generate-id(preceding-sibling::node[@id][1])" 
/> 

<xsl:copy-of select="key('kNode', generate-id(node[@id='1']))" /> 
+0

Finalmente fui a otra ruta, porque estoy fuera de XSLT, así que seleccioné todos los siguientes nodos hermanos, e iterar en ellos, y detener mi ciclo cuando me encuentro con el siguiente no vacio Acepto su respuesta como la más completa, porque ahora creo que no hay una sola línea en XPath para hacer lo que solicité. – glmxndr

+0

@subtenante: Erm - pero * hay * un XPath de una sola línea que hace eso bien en mi respuesta ?! – Tomalak

+0

sí, tiene razón, mi pregunta no es muy clara acerca del hecho de que los atributos de identificación que he puesto son solo para mostrar y explicar el problema. De hecho, no tengo los atributos de identificación en mi XML. – glmxndr

8

XPath 2.0 tiene los operadores < < 'y '>>', donde es node1 << node2 verdadero si el nodo1 precede al nodo2 en orden de documento. Así basa en que con XPath 2.0 en una hoja de estilo XSLT 2.0, donde el nodo actual es el nodo [@ id = '1'] se puede utilizar

following-sibling::node[not(text()) and . << current()/following-sibling::node[@od][1]] 

Eso también tiene la función actual() de XSLT, por lo es por eso que dije "con XPath 2.0 en una hoja de estilos XSLT 2.0". La sintaxis anterior es XPath puro, en una hoja de estilo XSLT necesitaría escapar '< <' como '& lt; & lt; '.

8

más simple que la respuesta aceptada:

//node[@id='1']/following-sibling::node[following::node[@id='2']] 
  • Encuentra un nodo en cualquier lugar cuyo ID es '1'
  • Ahora encontrar toda la siguiente hermano node elementos
  • ... pero sólo si los los elementos también tienen un node con id="2" en algún lugar después de ellos.

Se muestra en acción con un documento más clara prueba (y legales id valores):

xml = '<root> 
<node id="a"/><node id="b"/> 
<node id="c">content</node> 
<node id="d"/><node id="e"/><node id="f"/> 
<node id="g">content</node> 
<node id="h"/><node id="i"/> 
</root>' 

# A Ruby library that uses libxml2; http://nokogiri.org 
require 'nokogiri'; doc = Nokogiri::XML(xml) 

expression = "//node[@id='c']/following-sibling::node[following::node[@id='g']]" 
puts doc.xpath(expression) 
#=> <node id="d"/> 
#=> <node id="e"/> 
#=> <node id="f"/> 
+0

¿Por qué eligió el eje 'siguiente' aquí? Eso puede dar falsos positivos más adelante en la carretera, si el documento es más grande (además del eje siguiente es más lento que cualquier otro eje). – Abel

Cuestiones relacionadas