2009-06-03 16 views
5

Próbuję zgrupować dane rodzeństwa w pliku XML.Grupowanie XSLT Rodzeństwo

Dane:

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

Staram się produkować:

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

Używam następujące 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> 

Moim problemem jest to, że nie jest zatrzymanie Oprawa przetwarzanie węzły, gdy dotrze do następnej osi czasu

+0

nie tylko, że Twój XSLT nie będzie grupa same terminy, jeśli nie są one jeden po drugim. –

+0

Sprawdź mój soln ... będzie działał, nawet jeśli linie czasu są rozłożone w twoim xml zamiast być sekwencyjnym. –

+0

@Rashmi: skąd bierzesz wymóg zgrupowania tych samych osi czasu? Nie widzę żadnej sugestii, by wartości osi czasu nie były unikalne. – AnthonyWJones

Odpowiedz

9

Łatwo to zrobić, gdy prawdziwe są następujące (które zakładam, że jest):

  • wszystkie <timeline> s obrębie <competition> są unikalne
  • tylko w <fixture> s zaraz po dana <timeline> należą do to
  • nie ma <fixture> bez elementu <timeline> zanim

Ten XSLT 1.0 Rozwiązanie:

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

produkuje:

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

Uwaga Użycie <xsl:key> dopasować wszystkie <fixture> s należących do ("poprzedza") dana <timeline>.

Nieco krótsze, ale mniej oczywistym rozwiązaniem byłoby zmodyfikowaną tożsamość transform:

<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. Niezłe. :) – AnthonyWJones

+0

Bardzo ładna logika, proszę pana. Korzystam z kodu u, plus jeden. –

0

spróbować czegoś takiego:

<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

Nie rozumie następnego rodzeństwa. zamiast tego użyłem następującego-rodzeństwa :: * [1] – Xetius

+0

Kolejne-rodzeństwo nie jest osią, którą rozpoznaję? – AnthonyWJones

+0

Edytowałem, aby usunąć kolejne elementy rodzeństwa. –

0

Z pomocą g Andrieu miałem aby go dostać tylko kolejny element, a nie lista następujący: roztwór

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

Wygląda na to, że działa, ale jest to trochę trudne do naśladowania. – AnthonyWJones

1

G ANDRIEU nie działa, ponieważ niestety nie ma takich osi, jak "następne rodzeństwo".

I Alternatywnym rozwiązaniem byłoby następujące:

<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

Poniższy XSLT będzie działać, nawet jeśli same terminy są rozproszone w wielu miejscach. Dla np. w xml FOLL istnieją 2 wpisy na osi czasu 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> 

wyjścia:

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

Doskonała odpowiedź, dziękuję – Xetius

+0

Działa to tylko jeśli jest dokładnie jeden na dokument, co jest najprawdopodobniej błędnym założeniem. Nie ma potrzeby używania skrótów "//" w każdym miejscu, rozwiązanie przyniosłoby nawet korzyści, gdyby je całkowicie usunąć. – Tomalak

+0

Dzięki za sugestię Tomalak, zredagowałem xslt, aby obsługiwać wiele elementów konkurencji. –

3

Oto moja próba. Jedno z założonych przeze mnie założeń, które upraszcza to, że elementy osi czasu o określonej wartości tekstowej są już unikalne.

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

Powyższe jest edytowane w celu użycia current() zamiast zmiennej zgodnie z sugestią Tomalaka.

+0

Jest to jeden sposób, ale nie jest zbyt wydajny. +1 nadal :) Czy możesz naprawić przewijanie w poziomie? – Tomalak

+0

Nie jest to nieskuteczne, ale martwe proste, ale wolę twoje rozwiązanie. Skorygował trochę zawartość, aby zmniejszyć wcięcia, ale nadal przewija. – AnthonyWJones

+0

Dozwolone jest w XML umieszczanie tylu podziałów linii i spacji w dowolny atrybut. Pozwala to na ładny i czysty format XPath w wyrażeniu select, po prostu je trochę rozpakuj. – Tomalak