2009-05-16 11 views
9

Mam dynamiczne dokumentu XML, który reprezentuje struktury drzewa kategorii, ale nie tak przy użyciu ścieżki oddzielone atrybuty w dowolnej kolejności - tak:Tworzenie zagnieżdżonej struktury drzewa ze ścieżki w XSLT

<data>  
     <record ID="24" Name="category 1\sub category 1"/> 
     <record ID="26" Name="category 1"/>  
     <record ID="25" Name="category 1\sub category 1\sub category 2"/>  
     <record ID="27" Name="category 1\sub category 1\sub category 3"/>  
     ... 
    </data> 

muszę wymyślić rozwiązanie, które „normalizuje” XML, dzięki czemu jest ona przekształcona w coś takiego:

<data>  
     <record ID="26" Name="category 1">  
     <record ID="24" Name="sub category 1">  
      <record ID="25" Name="sub category 2"/> 
      <record ID="27" Name="sub category 3"/>  
     </record> 
     </record> 
     ... 
    </data> 

Zasadniczo zastanawiałem się, czy to jest coś XSLT może być w stanie rozwiązać i jak, a niż havin g zrobić programowo.

+0

@myso: „kategoria sub 3 "znajduje się na tym samym poziomie, co" podkategoria 2 "w wejściowym kodzie XML. Nie może być zagnieżdżony tak, jak pokazano w wyjściowym kodzie XML. – Tomalak

+0

Tak, mój błąd. Zmienili to. – mysomic

Odpowiedz

18

Jasne, nie ma problemu:

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

    <xsl:output indent="yes" /> 

    <xsl:template match="/data"> 
    <!-- copy the document element --> 
    <xsl:copy> 
     <!-- That's where we start: all "record" nodes that have no "\". --> 
     <xsl:apply-templates mode="recurse" select="/data/record[ 
     not(contains(@Name, '\')) 
     ]" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="record" mode="recurse"> 
    <xsl:param name="starting-path" select="''" /> 

    <!-- The record node and its ID attribute can be copied. --> 
    <xsl:copy> 
     <xsl:copy-of select="@ID" /> 

     <!-- Create the new "name" attribute. --> 
     <xsl:attribute name="Name"> 
     <xsl:value-of select="substring-after(@Name, $starting-path)" /> 
     </xsl:attribute> 

     <!-- Append a backslash to the current path. --> 
     <xsl:variable name="current-path" select="concat(@Name, '\')" /> 

     <!-- Select all "record" nodes that are one level deeper... --> 
     <xsl:variable name="children" select="/data/record[ 
     starts-with(@Name, $current-path) 
     and 
     not(contains(substring-after(@Name, $current-path), '\')) 
     ]" /> 

     <!-- ...and apply this template to them. --> 
     <xsl:apply-templates mode="recurse" select="$children"> 
     <xsl:with-param name="starting-path" select="$current-path" /> 
     </xsl:apply-templates> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Wyjście na moim systemie:

<data> 
    <record ID="26" Name="category 1"> 
    <record ID="24" Name="sub category 1"> 
     <record ID="25" Name="sub category 2"></record> 
     <record ID="27" Name="sub category 3"></record> 
    </record> 
    </record> 
</data> 

Uwaga że całe rozwiązanie oparte jest na założeniu, że wszystkie ścieżki są kanoniczne i nie zawierają końcowe backslashy .

Należy również pamiętać, że wszelkie niedopasowane/osierocone elementy "rekordu" nie będą na wyjściu (chyba że są na poziomie podstawowym, oczywiście).

Jeszcze jedno: tryb szablonowy ("rekurencyjny") nie jest koniecznie konieczny. Zawarłem to, ponieważ szablon robi coś specjalnego, i może istnieć szansa, że ​​istnieje inny szablon w twoim rozwiązaniu, który pasuje do "rekordowych" węzłów. W takim przypadku to rozwiązanie można wrzucić bez przerywania czegokolwiek innego. W przypadku samodzielnego rozwiązania tryby szablonów mogą zostać usunięte w dowolnym momencie.

Aha, i ostatnia rzecz: Jeśli chcesz dokument wynik zamawiane według nazwy, jak <xsl:sort> element z <xsl:apply-templates> (obu zdarzeń), tak jak poniżej:

<xsl:apply-templates select="..."> 
    <xsl:sort select="@Name" data-type="text" order="ascending" /> 
</xsl:apply-templates> 
+0

Działa idealnie. Co za niesamowita odpowiedź, dziękuję bardzo! – mysomic

+0

Nie ma za co. :-) – Tomalak

+6

Człowieku, po prostu rocka! – Cerebrus

Powiązane problemy