2009-12-31 38 views
5

Quiero usar XPath para obtener una lista de los nombres de todos los elementos que aparecen en un archivo XML. Sin embargo, no quiero que se repitan los nombres, por lo que un elemento con el mismo nombre que un elemento anterior no debe coincidir. Hasta ahora, tengo:XPath para obtener nombres de elementos únicos

*[not(local-name() = local-name(preceding::*))] 

Esto funciona bien, pero escupe duplicados. ¿Por qué escupió los duplicados y cómo puedo eliminarlos? (Estoy usando el motor XPath de Firefox).

+0

Sus asadores de código duplicados, porque la lista no está ordenada. Funcionaría en una lista ordenada. –

Respuesta

5

Obtendrá duplicados porque su filtro no está evaluando la forma en que cree que es.

El -nombre local() función devuelve el nombre local del primer nodo en el conjunto de nodos.

La única vez que su filtro de predicado funcionaría es si el elemento tenía el mismo nombre que el primer elemento anterior.

No creo que pueda lograr lo que quiere con un alma pura XPATH 1.0. Puede hacerlo en XPATH 2.0, pero eso no funcionaría con Firefox.

En XSLT puede utilizar el meunchien method para lograr lo que desea.

A continuación se muestra un ejemplo. No ha proporcionado ningún ejemplo XML, por lo que se mantenía muy genérica (por ejemplo // * coincide con todos los elementos en el documento):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"><xsl:output method="xml"/> 
<xsl:key name="names" match="//*" use="local-name(.)"/> 
<xsl:template match="/"> 
    <xsl:for-each select="//*[generate-id(.) = generate-id(key('names', local-name(.)))]"> 
     <!--Do something with the unique list of elements--> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 
+0

Gracias por la pista. Tiene razón sobre el nombre local() que devuelve el primer nodo.De hecho, quise marcar esto como la respuesta aceptada, pero hice clic en la marca de verificación incorrecta. Sin embargo, terminé haciendo el filtrado en javascript, por lo tanto, ambas respuestas son parte de mi solución. Gracias. – mawrya

1

Recomiendo primero obtener una lista de todos los elementos y luego recorrer la lista y agregarlos a un diccionario para detectar duplicados.

Por ejemplo, en pseudo-código:

var allElements = doc.select("//node()"); 
var distinctElementTypes = new object(); 
foreach (var elem in allElements) { 
    distinctElementTypes[elem.name] = elem.name; 
} 

Y ahora distinctElementTypes habrá un diccionario de nombres de elementos distintos.

+0

Gracias por la respuesta. Podría tomar este enfoque, pero xpath solo requiere una línea de código. Además, este es exactamente el tipo de problema para el que xpath está diseñado. Me gustaría saber qué está mal con el ejemplo dado, ya que quiero ampliar mi educación xpath. Por lo que puedo decir, debería funcionar, pero no es así. – mawrya

+0

No estoy seguro de por qué 'anterior' no funciona. ¿Podría ser que solo se compare con los nodos hermanos anteriores del nodo en cuestión en oposición a * todos * los nodos precedentes? – Eilon

+0

Ese sería el eje anterior-hermano ::. W3C dice: el eje anterior contiene todos los nodos en el mismo documento que el nodo de contexto que están antes del nodo de contexto en orden de documento, excluyendo cualquier antecesor y excluyendo nodos de atributo y nodos de espacio de nombres. Entonces, puedo ver por qué podría obtener duplicados si los elementos con el mismo nombre son antepasados, ¡pero también estoy obteniendo duplicados de elementos hermanos! – mawrya

6

Válido en XPath 2.0:

distinct-values(//*/name()) 
Cuestiones relacionadas