2009-05-16 6 views
9

ich ein dynamisches XML-Dokument, das eine Baumstruktur der Kategorien darstellt, wobei jedoch nicht so Pfad getrennt Attribute in beliebiger Reihenfolge - wie folgen aus:eine verschachtelte Baumstruktur von einem Pfad in XSLT erstellen

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

ich muss mit einer Lösung kommen, dass die XML ‚normalisiert‘, so dass es in etwa wie folgt transformiert:

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

Grundsätzlich ich mich gefragt, ob dies etwas XSLT ist zu adressieren möglicherweise in der Lage zu sein, und wie, und nicht als havin g es programmatisch zu tun.

+0

@myso: „Unterkategorie 3 "befindet sich auf derselben Ebene wie" Unterkategorie 2 "in Ihrer XML-Eingabe. Es kann nicht so verschachtelt werden, wie Sie es in Ihrer XML-Ausgabe anzeigen. – Tomalak

+0

Ja, mein Fehler. Habe es bearbeitet. – mysomic

Antwort

18

Klar, kein Problem:

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

Ausgang auf meinem System:

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

Beachten Sie, dass die gesamte Lösung auf der Annahme beruht, dass alle Pfade kanonisch sind und enthalten keine Hinterschrägstriche .

Beachten Sie auch, dass alle nicht übereinstimmenden/verwaisten "Record" -Elemente nicht in der Ausgabe enthalten sind (es sei denn, sie befinden sich natürlich auf der Stammebene).

Noch eine Sache: Der Template-Modus ("Recurse") ist nicht unbedingt notwendig. Ich habe es eingefügt, weil die Vorlage etwas ziemlich Spezielles vornimmt und es möglicherweise die Möglichkeit gibt, dass in Ihrer Lösung eine andere Vorlage vorhanden ist, die mit "Record" -Knoten übereinstimmt. In diesem Fall kann diese Lösung hineinfallen, ohne etwas anderes zu beschädigen. Für eine eigenständige Lösung können die Vorlagenmodi jederzeit gelöscht werden.

Oh, und das letzte, was: Wenn Sie das Ergebnis Dokument nach Namen geordnet werden, umfassen ein <xsl:sort> element mit den <xsl:apply-templates> (beide Vorkommen), etwa so:

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

Es funktioniert perfekt. Was für eine tolle Antwort, vielen Dank! – mysomic

+0

Gern geschehen. :-) – Tomalak

+6

Mann, du schaust einfach! – Cerebrus

Verwandte Themen