xsl regulares expresiones ejemplos consultas xml xslt xpath

xml - regulares - XPATH o XSL para unir dos conjuntos de nodos usando una comparación personalizada



xpath expresiones regulares (4)

Supongo que no podría hacer que XPath funcione. Empecé con el siguiente documento XML para inicializar los dos conjuntos de nodos:

<?xml version="1.0"?> <sets> <set> <text>/Geography/North America/California/San Francisco</text> <text>/Geography/Asia/Japan/Tokyo/Shinjuku</text> </set> <set> <text>/Geography/North America/</text> <text>/Geography/Asia/Japan/</text> </set> </sets>

Creo que esta hoja de estilo debería implementar la solución de Robert, pero solo recibo un recuento de ''1'':

<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:template match="/"> <xsl:variable name="set1" select="sets/set[1]/text/text()"/> <xsl:variable name="set2" select="sets/set[2]/text/text()"/> <xsl:value-of select="count($set1[starts-with(., $set2)])"/> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet>

Escribí una hoja de estilo que usa una plantilla recursiva y produce el recuento correcto de ''2'' con el documento de entrada dado, pero es mucho menos elegante que la respuesta de Robert. Si tan solo pudiera hacer que XPath funcione, siempre queriendo aprender.

EDITAR: También tengo acceso a las funciones de ESXLT .

Tengo dos conjuntos de nodos de tokens de cadenas. Un conjunto contiene valores como estos:

/Geography/North America/California/San Francisco /Geography/Asia/Japan/Tokyo/Shinjuku

El otro conjunto contiene valores como estos:

/Geography/North America/ /Geography/Asia/Japan/

Mi objetivo es encontrar un "partido" entre los dos. Se establece una coincidencia cuando cualquier secuencia en el conjunto 1 comienza con una cadena en el conjunto 2. Por ejemplo, se haría una coincidencia entre / Geography / North America / California / San Francisco y / Geography / North America / porque una cadena del conjunto 1 comienza con una secuencia del conjunto 2.

Puedo comparar cadenas usando comodines usando una extensión de terceros. También puedo usar una expresión regular dentro de un Xpath.

Mi problema es ¿cómo estructurar el Xpath para seleccionar usando una función entre todos los nodos de ambos conjuntos? XSL es también una opción viable.

Este XPATH:

count($set1[.=$set2])

Otorgaría el recuento de intersección entre set1 y set2, pero es una comparación de 1 a 1. ¿Es posible usar algún otro medio para comparar los nodos?

EDITAR: Lo hice funcionar, pero estoy haciendo trampa al usar algunas de las otras extensiones de terceros para obtener el mismo resultado. Todavía estoy interesado en otros métodos para hacer esto.


Esta:

<xsl:variable name="matches" select="$set1[starts-with(., $set2)]"/>

configurará $matches con un conjunto de nodos que contiene cada nodo en $set1 cuyo valor de texto comienza con el valor de texto de un nodo en $ set2. Eso es lo que estás buscando, ¿verdad?

Editar:

Bueno, estoy equivocado acerca de esto. Este es el por qué.

starts-with espera que sus dos argumentos sean cadenas. Si no lo son, los convertirá en cadenas antes de evaluar la función.

Si le da un conjunto de nodos como uno de sus argumentos, usa el valor de cadena del conjunto de nodos, que es el valor de texto del primer nodo en el conjunto. Entonces, en lo anterior, $set2 nunca se busca; solo se examina el primer nodo de la lista, por lo que el predicado solo encontrará nodos en $set1 que comiencen con el valor del primer nodo en $set2 .

Fui engañado porque este patrón (que he estado usando mucho en los últimos días) funciona:

<xsl:variable name="hits" select="$set1[. = $set2]"/>

Pero ese predicado está usando una comparación entre conjuntos de nodos, no entre valores de texto.

La forma ideal de hacerlo sería mediante el establecimiento de predicados. Es decir, "Quiero encontrar cada nodo en $set1 para el que hay un nodo en $set2 cuyo valor comienza con ..." y aquí es donde se rompe XPath. Comienza con qué? Lo que te gustaría escribir es algo como:

<xsl:variable name="matches" select="$set1[$set2[starts-with(?, .)]]"/>

solo que no hay una expresión que puedas escribir para el ? eso devolverá el nodo que está siendo probado actualmente por el predicado externo. (A menos que me esté perdiendo algo cegadoramente obvio)

Para obtener lo que desea, debe probar cada nodo individualmente:

<xsl:variable name="matches"> <xsl:for-each select="$set1"> <xsl:if test="$set2[starts-with(current(), .)]"> <xsl:copy-of select="."/> </xsl:if> </xsl:for-each> </xsl:variable>

Esa no es una solución muy satisfactoria porque evalúa un fragmento de árbol de resultados, no un conjunto de nodos. Tendrá que usar una función de extensión (como msxsl:node-set ) para convertir el RTF a un conjunto de nodos si desea usar la variable en una expresión XPath.


La última xsl:variable Robert es buena para obtener un fragmento de árbol de resultados que contenga los valores de texto coincidentes, pero a menos que (como sugiere) use extensiones EXSLT o MS para XSLT 1.0 para convertir el RTF a un conjunto de nodos, no podrá obtener un recuento de los nodos de texto coincidentes.

Esta es la hoja de estilo XSLT que mencioné en mi respuesta anterior que se repite sobre el documento de entrada de muestra que di para contar los nodos de texto en el conjunto 1 para los cuales un nodo en el conjunto 2 coincide con una parte o la totalidad:

<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output indent="yes" method="text"/> <xsl:template match="/"> <xsl:call-template name="count-matches"> <xsl:with-param name="set1-node" select="sets/set[1]/text[1]"/> <xsl:with-param name="set2-node" select="sets/set[2]/text[1]"/> <xsl:with-param name="total-count" select="0"/> </xsl:call-template> <xsl:text> </xsl:text> </xsl:template> <xsl:template name="count-matches"> <xsl:param name="set1-node"/> <xsl:param name="set2-node"/> <xsl:param name="total-count" select="0"/> <xsl:variable name="this-count"> <xsl:choose> <xsl:when test="contains($set1-node, $set2-node)"> <xsl:value-of select="1"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="0"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:choose> <xsl:when test="$set2-node/following-sibling::text"> <xsl:call-template name="count-matches"> <xsl:with-param name="set1-node" select="$set1-node"/> <xsl:with-param name="set2-node" select="$set2-node/following-sibling::text[1]"/> <xsl:with-param name="total-count" select="$total-count + $this-count"/> </xsl:call-template> </xsl:when> <xsl:when test="$set1-node/following-sibling::text"> <xsl:call-template name="count-matches"> <xsl:with-param name="set1-node" select="$set1-node/following-sibling::text[1]"/> <xsl:with-param name="set2-node" select="$set2-node/preceding-sibling::text[last()]"/> <xsl:with-param name="total-count" select="$total-count + $this-count"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$total-count + $this-count"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>

No es particularmente conciso, pero debido a que XSLT no permite que los programadores asignen nuevos valores a las variables ya definidas, a menudo es necesaria la recursión. No veo una forma en XSLT 1.0 para obtener un recuento del tipo solicitado por Zack usando xsl:for-each .


Existe una solución XSLT 1.0 simple y pura (no se necesitan extensiones) para encontrar el recuento de coincidencias :

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:template match="/"> <xsl:variable name="vStars"> <xsl:for-each select="*/regions/*"> <xsl:for-each select="/*/cities/*[starts-with(.,current())]"> <xsl:value-of select="''*''"/> </xsl:for-each> </xsl:for-each> </xsl:variable> <xsl:value-of select="string-length($vStars)"/> </xsl:template> </xsl:stylesheet>

Cuando se aplica esta transformación en el siguiente documento XML :

<t> <cities> <city>/Geography/North America/California/San Francisco</city> <city>/Geography/Asia/Japan/Tokyo/Shinjuku</city> </cities> <regions> <region>/Geography/North America/</region> <region>/Geography/Asia/Japan/</region> </regions> </t>

el resultado correcto es producido :

2

Tenga en cuenta que se produce un carácter (un asterisco) por cada coincidencia encontrada y todos estos asteriscos forman el contenido de la variable $vStars . Luego simplemente sacamos su string-length() .