2009-06-03 27 views
5

Estoy tratando de agrupar datos hermanos en un archivo XML.XSLT Agrupar hermanos

dado:

<?xml version="1.0" encoding="UTF-8"?> 
<data> 
    <competition> 
     <timeline>10:00</timeline> 
     <fixture>team a v team b</fixture> 
     <fixture>team c v team d</fixture> 
     <timeline>12:00</timeline> 
     <fixture>team e v team f</fixture> 
     <timeline>16:00</timeline> 
     <fixture>team g v team h</fixture> 
     <fixture>team i v team j</fixture> 
     <fixture>team k v team l</fixture> 
    </competition> 
</data> 

Estoy tratando de producir:

<?xml version="1.0" encoding="UTF-8"?> 
<data> 
    <competition> 
     <timeline time="10:00"> 
      <fixture>team a v team b</fixture> 
      <fixture>team c v team d</fixture> 
     </timeline> 
     <timeline time="12:00"> 
      <fixture>team e v team f</fixture> 
     </timeline> 
     <timeline time="16:00"> 
      <fixture>team g v team h</fixture> 
      <fixture>team i v team j</fixture> 
      <fixture>team k v team l</fixture> 
     </timeline> 
    </competition> 
</data> 

estoy usando el siguiente XSLT:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 

    <xsl:template match="competition" > 

     <xsl:apply-templates select="timeline" /> 

    </xsl:template> 

    <xsl:template match="timeline"> 
     <timeline> 
      <xsl:attribute name="time" > 
       <xsl:value-of select="." /> 
      </xsl:attribute> 

      <xsl:apply-templates select="following-sibling::*" mode="copy"/> 

     </timeline> 
    </xsl:template> 

    <xsl:template match="fixture" mode="copy"> 
     <fixture> 
      <xsl:value-of select="." /> 
     </fixture> 
    </xsl:template> 

    <xsl:template match="timeline" mode="copy"> 
     <xsl:apply-templates select="following-sibling::*" mode="null" /> 
    </xsl:template> 

    <xsl:template match="*" mode="null"> 
    </xsl:template> 
</xsl:stylesheet> 

Mi problema es que no se detiene accesorio de procesamiento nodos cuando llega a la siguiente línea de tiempo

+0

No sólo eso, su voluntad no XSLT grupo mismas líneas de tiempo si no están uno detrás de otro. –

+0

Revisa mi sol. ... funcionará incluso si las líneas de tiempo están distribuidas en tu xml en lugar de ser secuenciales. –

+0

@Rashmi: ¿de dónde manejas el requisito de agrupar las mismas líneas de tiempo a partir de? No veo ninguna sugerencia de que los valores de la línea de tiempo no sean únicos. – AnthonyWJones

Respuesta

9

Esto es fácil de hacer cuando se cumple lo siguiente (que supongo que es):

  • todos <timeline> s dentro de un <competition> son únicos
  • sólo el <fixture> s justo después de un determinado <timeline> pertenecen a que
  • no hay <fixture> sin un elemento <timeline> antes de que

Este XSLT 1.0 solución:

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 

    <xsl:key name="kFixture" 
      match="fixture" 
      use="generate-id(preceding-sibling::timeline[1])" 
    /> 

    <xsl:template match="data"> 
    <xsl:copy> 
     <xsl:apply-templates select="competition" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="competition"> 
    <xsl:copy> 
     <xsl:apply-templates select="timeline" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="timeline"> 
    <xsl:copy> 
     <xsl:attribute name="time"> 
     <xsl:value-of select="." /> 
     </xsl:attribute> 
     <xsl:copy-of select="key('kFixture', generate-id())" /> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

produce:

<data> 
    <competition> 
    <timeline time="10:00"> 
     <fixture>team a v team b</fixture> 
     <fixture>team c v team d</fixture> 
    </timeline> 
    <timeline time="12:00"> 
     <fixture>team e v team f</fixture> 
    </timeline> 
    <timeline time="16:00"> 
     <fixture>team g v team h</fixture> 
     <fixture>team i v team j</fixture> 
     <fixture>team k v team l</fixture> 
    </timeline> 
    </competition> 
</data> 

Nota el uso de un <xsl:key> para que coincida con todos los <fixture> s que pertenecen a ("van precedidas de") un hecho <timeline>.

Una solución un poco más corto, pero menos obvia sería una identidad modificada transformar:

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 

    <xsl:key name="kFixture" 
      match="fixture" 
      use="generate-id(preceding-sibling::timeline[1])" 
    /> 

    <xsl:template match="* | @*"> 
    <xsl:copy> 
     <xsl:apply-templates select="*[not(self::fixture)] | @*" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="timeline"> 
    <xsl:copy> 
     <xsl:attribute name="time"> 
     <xsl:value-of select="." /> 
     </xsl:attribute> 
     <xsl:copy-of select="key('kFixture', generate-id())" /> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 
+0

+1. Buena esa. :) – AnthonyWJones

+0

Muy buena lógica señor. Hago uso de tu código, más uno. –

0

intentar algo así como que:

<xsl:template match="timeline"> 
    <timeline> 
      <xsl:attribute name="time" > 
        <xsl:value-of select="." /> 
      </xsl:attribute> 

      <xsl:apply-templates select="following-sibling::*[name()=fixture][1]" /> 

    </timeline> 
</xsl:template> 

<xsl:template match="fixture"> 
    <fixture> 
      <xsl:value-of select="." /> 
    </fixture> 
    <xsl:apply-templates select="following-sibling::*[name()=fixture][1]" /> 
</xsl:template> 
+0

No entiende próximo hermano. en su lugar usé following-sibling :: * [1] – Xetius

+0

next-sibling no es un eje que reconozca? – AnthonyWJones

+0

He editado para eliminar las cosas del próximo hermano. –

0

Con la ayuda de g andrieu que tenía que hacerlo sólo recibe el siguiente elemento y no la lista siguiente: solución

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 

    <xsl:template match="competition" > 

     <xsl:apply-templates select="timeline" /> 

    </xsl:template> 

    <xsl:template match="timeline"> 
     <timeline> 
      <xsl:attribute name="time" > 
       <xsl:value-of select="." /> 
      </xsl:attribute> 

      <xsl:apply-templates select="following-sibling::*[1]" mode="copy"/> 

     </timeline> 
    </xsl:template> 

    <xsl:template match="fixture" mode="copy"> 
     <fixture> 
      <xsl:value-of select="." /> 
     </fixture> 
     <xsl:apply-templates select="following-sibling::*[1]" mode="copy"/> 
    </xsl:template> 

    <xsl:template match="timeline" mode="copy" /> 

</xsl:stylesheet> 
+0

Parece que funciona, pero es un poco difícil de seguir. – AnthonyWJones

1

G Andrieu no funciona, ya que no hay ejes como 'próximo hermano' desafortunadamente.

y Alternativa solución sería la siguiente:

<xsl:template match="timeline"> 
<timeline> 
    <xsl:attribute name="time" > 
    <xsl:value-of select="." /> 
    </xsl:attribute> 

    <xsl:apply-templates select="following-sibling::*[local-name()='fixture' and position()=1]" /> 

</timeline> 
</xsl:template> 

<xsl:template match="fixture"> 
    <fixture> 
     <xsl:value-of select="." /> 
    </fixture> 
    <xsl:apply-templates select="following-sibling::*[local-name()='fixture' and position()=1]" /> 
</xsl:template> 
1

La siguiente XSLT funcionará incluso si mismas líneas de tiempo se encuentran dispersos en varios lugares. Por ej. en el xml ss hay 2 entradas para la línea de tiempo 10:00

<?xml version="1.0" encoding="UTF-8"?> 
<data> 
    <competition> 
     <timeline>10:00</timeline> 
     <fixture>team a v team b</fixture> 
     <fixture>team c v team d</fixture> 
     <timeline>12:00</timeline> 
     <fixture>team e v team f</fixture> 
     <timeline>16:00</timeline> 
     <fixture>team g v team h</fixture> 
     <fixture>team i v team j</fixture> 
     <fixture>team k v team l</fixture> 
     <timeline>10:00</timeline> 
     <fixture>team a v team b new</fixture> 
     <fixture>team c v team d new</fixture> 
    </competition> 
</data> 

Xslt:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 
    <xsl:key name="TimelineDistint" match="timeline" use="."/> 

    <xsl:template match="data"> 
     <xsl:apply-templates select="competition"/> 
    </xsl:template> 

    <xsl:template match="competition"> 
     <data> 
      <competition> 
       <xsl:for-each select="timeline[generate-id() = generate-id(key('TimelineDistint', .)[1])]"> 
        <timeline> 
         <xsl:variable name="varTimeline" select="."/> 
         <xsl:attribute name="time"><xsl:value-of select="normalize-space(.)"/></xsl:attribute> 
         <xsl:for-each select="../fixture[preceding::timeline[1] = $varTimeline]"> 
          <fixture> 
           <xsl:value-of select="normalize-space(.)"/> 
          </fixture> 
         </xsl:for-each> 
        </timeline> 
       </xsl:for-each> 
      </competition> 
     </data> 
    </xsl:template> 
</xsl:stylesheet> 

salida:

<?xml version="1.0" encoding="UTF-8"?> 
<data> 
    <competition> 
     <timeline time="10:00"> 
      <fixture>team a v team b</fixture> 
      <fixture>team c v team d</fixture> 
      <fixture>team a v team b new</fixture> 
      <fixture>team c v team d new</fixture> 
     </timeline> 
     <timeline time="12:00"> 
      <fixture>team e v team f</fixture> 
     </timeline> 
     <timeline time="16:00"> 
      <fixture>team g v team h</fixture> 
      <fixture>team i v team j</fixture> 
      <fixture>team k v team l</fixture> 
     </timeline> 
    </competition> 
</data> 
+0

Excelente respuesta, gracias – Xetius

+0

Esto solo funciona si es exactamente un por documento, lo cual es probablemente una suposición errónea. No es necesario utilizar la taquigrafía "//" en todas partes, la solución incluso se beneficiaría si los elimina por completo. – Tomalak

+0

Gracias por su sugerencia Tomalak, he editado el xslt para admitir múltiples elementos de la competencia. –

3

Aquí está mi intento. Una suposición que he hecho que simplifica las cosas es que los elementos de la línea de tiempo con un valor de texto específico ya son únicos.

<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" encoding="UTF-8" /> 

    <xsl:template match="/data"> 
    <data> 
     <xsl:apply-templates select="competition" /> 
    </data> 
    </xsl:template> 

    <xsl:template match="competition"> 
    <xsl:for-each select="timeline"> 
     <timeline time="{text()}"> 
     <xsl:copy-of 
      select="./following-sibling::fixture[count(preceding-sibling::timeline[1] | current()) = 1]" /> 
     </timeline> 
    </xsl:for-each> 
    </xsl:template> 

</xsl:stylesheet> 

Lo anterior se edita para usar current() en lugar de una variable según la sugerencia de Tomalak.

+0

Esta es una forma de hacerlo, aunque no demasiado eficiente. +1 aún :) ¿Puedes arreglar el desplazamiento horizontal? – Tomalak

+0

No no es eficiente pero está muerto simple, pero prefiero su solución. Ajustó el contenido un poco para reducir la sangría pero aún se desplaza. – AnthonyWJones

+0

Está permitido en XML poner tantos saltos de línea y espacios en blanco en un atributo como desee. Esto permite un formato limpio y agradable de XPath en su expresión de selección, simplemente divídalo un poco. – Tomalak