2010-06-10 65 views
5

Tengo que seleccionar solo registros únicos desde un documento XML, en el contexto de un bucle <xsl:for-each>. Estoy limitado por Visual Studio para usar XSL 1.0.Seleccionar registros únicos en XSLT/XPath

<availList> 
     <item> 
      <schDate>2010-06-24</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>13:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-24</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>13:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-25</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>12:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-26</schDate>    
      <schFrmTime>13:00:00</schFrmTime> 
      <schToTime>14:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
     <item> 
      <schDate>2010-06-26</schDate>    
      <schFrmTime>10:00:00</schFrmTime> 
      <schToTime>12:00:00</schToTime> 
      <variousOtherElements></variousOtherElements> 
     </item> 
    </availList> 

La singularidad debe basarse en el valor de los tres elementos secundarios: schDate, schFrmTime y schToTime. Si dos elementos item tienen los mismos valores para los tres elementos secundarios, son duplicados. En el XML anterior, los elementos uno y dos son duplicados. El resto es único. Como se indicó anteriormente, cada elemento contiene otros elementos que no deseamos incluir en la comparación. 'Unicidad' debería ser un factor de esos tres elementos, y solo de ellos.

he tratado de lograr esto a través de lo siguiente:

availList/item[not(schDate = preceding:: schDate and schFrmTime = preceding:: schFrmTime and schToTime = preceding:: schToTime)] 

La idea detrás de esto es para seleccionar registros en los que no hay ningún elemento que precede a la misma schDate, schFrmTime y schToTime. Sin embargo, su salida es falta el último elemento. Esto se debe a que mi XPath excluye elementos en los que coinciden todos los valores del elemento secundario en el documento anterior completo. Ningún item coincide con todos los elementos secundarios del último elemento, pero dado que el valor de cada elemento está presente individualmente en otro elemento, el último elemento queda excluido.

que podría conseguir el resultado correcto comparando todos los valores del niño como una cadena concatenada a los mismos valores concatenados para cada elemento precedente. ¿Alguien sabe de una forma en que podría hacer esto?

+0

Buena pregunta (+1). Vea mi respuesta para una solución XPath y XSLT. –

+1

El método que usa la tecla() se conoce comúnmente como el método de Muenchian: http://www.jenitennison.com/xslt/grouping/muenchian.html –

Respuesta

4

I. Como una sola expresión XPath:

/*/item[normalize-space() and not(. = preceding-sibling::item)] 

II.Más eficientes (XSLT) de aplicación, utilizando las teclas:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:key name="kItemByVal" match="item" use="."/> 

<xsl:template match="/"> 
    <xsl:copy-of select= 
    "*/item[generate-id() = generate-id(key('kItemByVal', .))] 
    "/> 
</xsl:template> 
</xsl:stylesheet> 

Tanto I y II, cuando se aplica sobre el documento XML proporcionado seleccionar correctamente/copiar los siguientes nodos:

<item><schDate>2010-06-24</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>13:00:00</schToTime></item> 
<item><schDate>2010-06-25</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>12:00:00</schToTime></item> 
<item><schDate>2010-06-26</schDate><schFrmTime>13:00:00</schFrmTime><schToTime>14:00:00</schToTime></item> 
<item><schDate>2010-06-26</schDate><schFrmTime>10:00:00</schFrmTime><schToTime>12:00:00</schToTime></item> 

actualización : En caso de que <item> tenga otros hijos, entonces esta transformación:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 

    <xsl:key name="kItemBy3Children" match="item" 
    use="concat(schDate, '+', schFrmTime, '+', schToTime)"/> 

<xsl:template match="/"> 
     <xsl:copy-of select= 
     "*/item[generate-id() 
       = generate-id(key('kItemBy3Children', 
           concat(schDate, 
             '+', schFrmTime, 
             '+', schToTime) 
           ) 
          ) 
       ] 
     "/> 
</xsl:template> 
</xsl:stylesheet> 

produce el resultado deseado.

+0

Dimitre, Muchas gracias por su respuesta. Sin embargo, me temo que no funcionará para mi caso. Pido disculpas por no haber sido muy claro al escribir mi pregunta (posteriormente la he editado). El problema es que, en realidad, mis elementos 'item' también contienen varios otros subelementos que no deben tenerse en cuenta para determinar si los elementos están seleccionados o no. En realidad, no estoy buscando la singularidad "real", estoy buscando exclusividad solo en ciertos valores de elementos secundarios. Sin embargo, estoy seguro de que tu respuesta será valiosa para otros. Dan –

+1

@ Daniel-I-S: He actualizado mi respuesta con una solución al problema modificado. –

+2

Esa es una gran respuesta; muchas gracias. –

2

La técnica que he visto es hacer esto en dos pasos: ordenar los elementos por los tres campos clave, y luego comparar cada elemento con el elemento anterior (en lugar de todos los elementos anteriores).

¿Es práctico para usted realizar dos transformaciones por separado? Hace el problema mucho más fácil.

Vi la técnica en una edición anterior de Michael Kay's XSLT book. Puede encontrarlo en algunos de sus códigos de muestra allí.

Cuestiones relacionadas