2009-06-03 13 views
5

Sto provando a raggruppare i dati di pari livello in un file XML.XSLT Raggruppamento di fratelli

Dato:

<?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> 

sto cercando di produrre:

<?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> 

Sto usando il seguente 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> 

Il mio problema è che non si ferma apparecchio di elaborazione nodi quando arriva alla timeline successiva

+0

Non solo, la tua volontà non XSLT gruppo stesse scadenze se non sono uno dopo l'altro. –

+0

Controlla il mio soln ... funzionerà anche se le timeline sono distribuite nel tuo xml invece di essere sequenziali. –

+0

@Rashmi: da dove guidi il requisito per raggruppare insieme le stesse linee temporali? Non vedo alcun suggerimento che i valori della timeline non siano unici. – AnthonyWJones

risposta

9

Questo è facile da fare quando il seguente è vera (che suppongo che sia):

  • tutti <timeline> s all'interno di un <competition> sono unici
  • solo il <fixture> s destra dopo un certo <timeline> appartengono esso
  • non c'è <fixture> senza un elemento <timeline> prima

Questo XSLT 1.0 Soluzione:

<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 l'uso di un <xsl:key> per abbinare tutto <fixture> s che appartengono a ("sono preceduti da") un dato <timeline>.

Una soluzione un po 'più breve, ma meno evidente sarebbe un'identità modificata trasformare:

<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. Ben fatto. :) – AnthonyWJones

+0

Molto bella logica signore. Faccio uso del tuo codice, più uno. –

0

provare qualcosa di simile:

<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

Non capisce fratello successivo. invece ho usato follow-sibling :: * [1] – Xetius

+0

next-sibling non è un asse che riconosco? – AnthonyWJones

+0

Ho modificato per rimuovere la roba successiva. –

0

Con l'aiuto di g andrieu ho dovuto fare solo ottenere l'elemento successivo e non la lista seguente: soluzione

<?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

Sembra che funzioni, ma è un po 'difficile da seguire. – AnthonyWJones

1

G Andrieu non funziona, poiché non ci sono tali assi come 'prossimo-fratello' sfortunatamente.

E alternativa soluzione potrebbe essere la seguente:

<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

Il seguente XSLT funzionerà anche se stesse scadenze sono sparsi in più posti. Ad es. nel xml foll ci sono 2 voci per linea temporale 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> 

uscita:

<?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

Risposta eccellente, grazie – Xetius

+0

Questo funziona solo se è esattamente uno per documento, che è probabilmente un presupposto errato. Non c'è bisogno di usare la scorciatoia "//" dappertutto, la soluzione sarebbe vantaggiosa anche se li avessi completamente rimossi. – Tomalak

+0

Grazie per il tuo suggerimento Tomalak, ho modificato xslt per supportare più elementi di competizione. –

3

Ecco il mio tentativo. Un'ipotesi che ho fatto che semplifica le cose è che gli elementi della timeline con un valore di testo specifico sono già unici.

<?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> 

Quanto sopra è modificato per utilizzare current() anziché una variabile come da suggerimento di Tomalak.

+0

Questo è un modo per farlo, anche se non eccessivamente efficiente. +1 ancora :) Puoi correggere lo scorrimento orizzontale? – Tomalak

+0

No non efficiente ma dead simple tuttavia preferisco la tua soluzione. Aggiustato il contenuto un po 'per ridurre il rientro ma continua a scorrere. – AnthonyWJones

+0

In XML è consentito inserire tutte le interruzioni di riga e lo spazio bianco in un attributo a piacere. Ciò consente di ottenere un formato piacevole e pulito dell'XPath nell'espressione di selezione, basta spezzarlo un po '. – Tomalak