Blog!

Busqueda de información

Manejo de agrupaciones en XSLT

Ratio: 5 / 5

Inicio activadoInicio activadoInicio activadoInicio activadoInicio activado
 

¿Alguna vez has querido agrupar listas de información en XSLT? ¿Cómo podemos organizar en grupos una lista de elementos? Este es un problema común en XSLT 1.0

 

Supongamos que tenemos un mensaje en XML de los partidos en los que participo un jugador de futbol a lo largo de todo el torneo:

 

 

De este mensaje, queremos agrupar los campos por jugador y listar los partidos que jugó, el problema es cómo convertir esta entrada en una serie de listas, agrupados por jugador, para poder obtener el siguiente mensaje de salida.

 

 

 

 

Una opción es basarnos en el método de Muenchian el cual es utilizado para la agrupación de nodos de acuerdo a una propiedad en particular que es asignada a una llave. Si hay muchos nodos que tienen el mismo valor de la llave, entonces todos esos nodos se recuperan cuando se utiliza ese valor de la llave.

 

Otra alternativa para la agrupación es identificar los diferentes jugadores que hay en el mensaje.

 

/partidos/partido[not(jugador = preceding-sibling::partido/jugador)]

 

Una vez que estos jugadores han sido identificados, se pueden obtener todos los partidos que tienen el mismo jugador.

<xsl:apply-templates select="/partidos/partido[jugador = current()/jugador]"/>

 

El problema con este método es que usa XPath's que tardan mucho en procesarse para XML’s muy grandes. Cuando se busca en los elementos anteriores con “preceding-sibling” toma mucho tiempo si está en el final de los registros, lo mismo ocurre cuando obtenemos todos los partidos de un cierto jugador ya que implica mirar un solo partido en cada ocasión.

 

En XSLT 2.0 ya se introdujo la etiqueta xsl:for-each-group que realiza la agrupación de forma mas directa.

 

 

Utilizando la primera opción descrita para resolver el problema, se deben realizar los siguientes pasos:

 

Primero generamos una llave que asigna a cada partido un valor de llave que en este caso es el jugador y que nos servirá para agrupar los partidos por jugador.

 

En la definición de la llave los nodos que queremos agrupar deben coincidir con el patrón especificado en el atributo “match” y el valor de la llave que queremos usar es la que está dado por el atributo “use

 

<xsl:key name="jugador-key" match="/partidos/partido" use="jugador"/>

 

Una vez definida la llave podemos acceder rápidamente a todos los partidos de determinado jugador con la función key(), por ejemplo:

 

key('jugador-key', 'esteban')

 

Esta expresión regresará todos los partidos del jugador “esteban”

 

 

El siguiente paso es iterar todos los nodos y solo procesar el primer nodo de los devueltos por cada llave para así poder quitar los duplicados y solo obtener un nodo por jugador.

 

 

Sabemos que un partido será parte de una lista de nodos que se obtengan cuando utilizamos la llave con el valor de su jugador, lo que no sabemos es si este partido es el primero en esa lista o tiene una posición posterior, nosotros sólo estamos interesados ​​en los nodos que se encuentran en primer lugar de las listas devueltas por la llaves.

 

Para determinar si un nodo es el primero devuelto por la llave se comparan el nodo del partido actual con el primer nodo de la lista de nodos devuelta por el valor de la llave del jugador, para esto nos apoyaremos de la función generate-id() la cual nos permite comparar los identificadores únicos generados para los dos nodos.

 

Todo lo anterior se hace utilizando el elemento xsl:for-each que actúa sobre una expresión XPath muy particular:

 

<xsl:for-each select="/partidos/partido[generate-id()=generate-id(key(' jugador-key ',jugador)[1])]">

 

El detalle de lo que hace la expresión anterior es:

 

  • Obtener el jugador del nodo actual
  • Utiliza la llave para identificar todos los nodos con el mismo jugador
  • Seleccionar el primer nodo de cada llave
  • Generar una cadena para identificar de forma única el primer nodo de cada llave
  • Generar una cadena para identificar de forma única el nodo actual
  • Comparar las dos cadenas. Si son iguales, entonces el nodo actual es el primer nodo de la llave, de lo contrario, es un duplicado.

 

El último paso es recuperar el conjunto de nodos que corresponde a cada llave con el valor de cada jugador, adicionalmente se pueden ordenar los nodos en el orden que se requiera.

 

<xsl:for-each select="key(' jugador-key ',jugador)">

<xsl:sort select="local"/>

 

Recopilando lo anterior, la trasformación quedaría de la siguiente forma.

 

 

Con esto damos por concluido el tema.

 

Cualquier duda o comentario puedes contactarnos a info[at]baware.com.mx o acramirez[at]baware.com.mx

 

Log in