martes, 12 de febrero de 2013

Select Distinct in XSL, using xsl:key

Para esta entrada les comparto una forma de obtener una lista de nodos (distintos) a partir de una variable usando XPATH sobre XSLT.

Podríamos comparar esta sentencia como un "Select Distinct", ese que usamos en sql para obtener registros distintos de acuerdo a los criterios de búsqueda. Bueno entonces este es el proceso:

Primero, usé un ejemplo que hallé en Internet, en esta url.

Segundo, esta paginita en la que podemos hacer las pruebas de transformación online 

Por último, la mágia:

tenemos entonces un xml como este:
<?xml version="1.0" encoding="UTF-8"?>
<DropDownItems>
        <DropDownItem>
                <direction>E</direction>
                <street>10TH</street>
                <price>
                   <tax>10p</tax>
                </price>
                <streetType>AVE</streetType>
        </DropDownItem>
        <DropDownItem>
                <direction>E</direction>
                <street>11TH</street>
                <price>
                   <tax>10p</tax>
                </price>
                <streetType>DR</streetType>
        </DropDownItem>
       <DropDownItem>
                <direction>E</direction>
                <street>10TH</street>
                <price>
                   <tax>10p</tax>
                </price>
                <streetType>AAR</streetType>
        </DropDownItem>

</DropDownItems>

Ahora queremos obtener simplemente los nodos cuyo valor "X" sea distinto.
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="by-street" match="DropDownItem" use="street"/>
  <xsl:template match="DropDownItems">
    <xsl:copy>
      <xsl:copy-of select="DropDownItem[generate-id() = generate-id(key('by-street', street)[1])]"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>
Entonces usamos el código de la página donde obtuve el ejemplo. Pero realmente no se trata de mostrar lo mismo, primero una breve explicación, y un ejemplo el cual me llevó a publicar mi propio ejemplo.

vemos un xsl:key, el cual nos va a permitir indexar los nodos de acuerdo al valor de (un atributo || nodo), para lo cual hay que tener en cuenta lo siguiente:
1. xsl:key, se usa a nivel global, es decir, que no se puede crear dentro de un template
2. name, lo usamos para referenciar nuestra key
3. match, es la ruta relativa del nodo que contiene nuestro valor para la key
4 use, es el nodo dentro del match, o atributo del match que usaremos como filtro, entonces si es un nodo, simplemente le colocamos el nombre, y de ser un atributo le adicionamos como prefijo un "@" ej: 

<xsl:key name="by-attr" match="DropDownItem" use="@street"/>
select="DropDownItem[generate-id() = generate-id(key('by-attr', @street)
Ahora, supongamos que nuestro atributo o nodo para filtro no es hijo del nodo que necesitamos obtener, entonces hay que modificar un poco nuestro XPATH, para obtener el resultado:

primero la key:  

<xsl:key name="by-street" match="DropDownItem/price" use="tax"/>

ahora el select:

select="DropDownItem[price[generate-id() = generate-id(key('by-street', tax)[1])]]">

Se puede ver que ahora, que nuestro filtro será el nodo TAX, para lo que debemos definir nuestra key apuntando a esa ruta, y luego ajustamos nuestro select, para que el XPATH entienda que nuestro nodo de resultado debe ser un DropDownItem, y la búsqueda de la key es en uno de sus hijos llamado price.

Espero les sirva este aporte.