2009-07-01 9 views
1

Ich habe eine xml der Form:Wie erstellt man Teilmengen eines einzelnen Satzes von Elementen mit XSLT?

<Set> 
    <Element name="Superset1_Set1_Element1"/> 
    <Element name="Superset1_Set1_Element2"/> 
    <Element name="Superset1_Set2_Element1"/> 
    <Element name="Superset2_Set1_Element1"/> 
    <Element name="Superset2_Set2_Element1"/> 
</Set> 

Ich wünschte, es in einem XML-der Form zu transformieren:

<Superset name="Superset1"> 
    <Set name="Set1"> 
     <Element name="Element1"/> 
     <Element name="Element2"/> 
    </Set> 
    <Set name="Set2"> 
     <Element name="Element1"/> 
    </Set> 
</Superset> 
<Superset name="Superset2"> 
    <Set name="Set1"> 
     <Element name="Element1"/> 
    </Set> 
    <Set name="Set2"> 
     <Element name="Element1"/> 
    </Set> 
</Superset> 

Wie dies mit XSLT kann getan werden?

Vielen Dank im Voraus!

Antwort

5

Dies kann mit der folgenden XSLT 1.0-Transformation gelöst werden:

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

    <xsl:output method="xml" indent="yes" /> 

    <!-- this key selects elements by their "Superset" name --> 
    <xsl:key name="kElementBySuperset" match="Element" use=" 
    substring-before(@name, '_')" 
    /> 

    <!-- this key selects elements by their "Superset_Set" name --> 
    <xsl:key name="kElementBySet" match="Element" use=" 
    concat(
     substring-before(@name, '_'), 
     '_', 
     substring-before(substring-after(@name, '_'), '_') 
    ) 
    " /> 

    <!--- initalize output (note the template modes) --> 
    <xsl:template match="Set"> 
    <xsl:apply-templates select="Element" mode="Superset"> 
     <xsl:sort select="@name" /> 
    </xsl:apply-templates> 
    </xsl:template> 

    <!-- output <Superset> elements, grouped by name --> 
    <xsl:template match="Element" mode="Superset"> 
    <xsl:variable name="vSupersetName" select=" 
     substring-before(@name, '_') 
    " /> 

    <xsl:if test=" 
     generate-id() 
     = 
     generate-id(key('kElementBySuperset', $vSupersetName)[1]) 
    "> 
     <Superset name="{$vSupersetName}"> 
     <xsl:apply-templates 
      select="key('kElementBySuperset', $vSupersetName)" 
      mode="Set" 
     > 
      <xsl:sort select="@name" /> 
     </xsl:apply-templates> 
     </Superset> 
    </xsl:if> 
    </xsl:template> 

    <!-- output <Set> elements, grouped by name --> 
    <xsl:template match="Element" mode="Set"> 
    <xsl:variable name="vSetName" select=" 
     concat(
     substring-before(@name, '_'), 
     '_', 
     substring-before(substring-after(@name, '_'), '_') 
    )" 
    /> 

    <xsl:if test=" 
     generate-id() 
     = 
     generate-id(key('kElementBySet', $vSetName)[1]) 
    "> 
     <Set name="{substring-after($vSetName, '_')}"> 
     <xsl:apply-templates 
      select="key('kElementBySet', $vSetName)" 
      mode="Element" 
     > 
      <xsl:sort select="@name" /> 
     </xsl:apply-templates> 
     </Set> 
    </xsl:if> 
    </xsl:template> 

    <!-- output <Element> elements --> 
    <xsl:template match="Element" mode="Element"> 
    <xsl:variable name="vElementName" select=" 
     substring-after(
     substring-after(@name, '_'), 
     '_' 
    ) 
    " /> 

    <Element name="{$vElementName}" /> 
    </xsl:template> 

</xsl:stylesheet> 

Ausgabe auf mein System, wenn auf Ihr Eingabedokument angewandt:

<Superset name="Superset1"> 
    <Set name="Set1"> 
    <Element name="Element1" /> 
    <Element name="Element2" /> 
    </Set> 
    <Set name="Set2"> 
    <Element name="Element1" /> 
    </Set> 
</Superset> 
<Superset name="Superset2"> 
    <Set name="Set1"> 
    <Element name="Element1" /> 
    </Set> 
    <Set name="Set2"> 
    <Element name="Element1" /> 
    </Set> 
</Superset> 

Es ist erwähnenswert, dass diese Lösung Fall empfindlich ist. Ich nehme an, das ist in Ihrem Fall wünschenswert (oder zumindest nicht schädlich). Wenn Fall-Unempfindlichkeit erforderlich ist, dann eine Handvoll dieser Beregnung wäre notwendig geworden (wobei „...“ muss natürlich durch die fehlenden Buchstaben ersetzt werden):

translate($anyvalue, 'ABC…XYZ', 'abc…xyz') 

ich, dass vermieden, weil es sehr eintönig ist und macht die Lösung (noch mehr) unklar.

Weiterführende Literatur: Eine meiner Lösungen, die einen ähnlichen zweistufigen tut Gruppierung mit zwei <xsl:key> s hier:

XSLT 3-level grouping on attributes

Es auf die Interna etwas ausführlicher, und es enthält eine lange Erklärung von <xsl:key>, die ich hier vermeiden möchte. ;-)

+0

Randnotiz: Obwohl das * viel komplizierter aussieht als die Lösung von @ annakata, ist es in der Tat absolut dasselbe. Es verwendet nur separate Vorlagen anstelle von verschachtelten -Aufrufen.Und viele Zeilenumbrüche, die nicht unbedingt notwendig sind. ;-) – Tomalak

+0

Es liegt ein kleiner Kompilierfehler vor. Ändern Sie "kElementBySuperset" in "kElementBySup" und dann wird das funktionieren. – Aamir

+0

Ja, nur gesehen und behoben. Dies erhalten Sie, wenn Sie den Code hier bearbeiten * nachdem * Sie die getestete Version aus Ihrem Texteditor eingefügt haben ... – Tomalak

3

als Referenz erhalten, aber ich dringend die Verwendung von Vorlagen (dh Tomalak-Lösung), wo möglich, zur besseren Lesbarkeit allein vorschlagen ...


sicherlich möglich, aber eigentlich schwerer, als ich wegen der zu erwartenden Sätze zweiter Ordnung und die doppelten Unterstriche - das Folgende könnte sicherlich verbessert werden, wenn die "Namen" -Werte ein freundlicheres Format hätten.

<xsl:key name="supers" match="Set/Element" use="substring-before(@name,'_')"/> 
<xsl:key name="sets" match="Set/Element" use="concat(substring-before(@name,'_'),'_',substring-before(substring-after(@name,'_'),'_'))"/> 

<xsl:template match="/"> 
    <xsl:for-each select="Set/Element[generate-id() = generate-id(key('supers',substring-before(@name,'_'))[1])]">  
     <xsl:variable name="super" select="substring-before(@name,'_')"/> 
      <Superset name="{$super}">   
      <xsl:for-each select="//Set/Element[generate-id() = generate-id(key('sets',concat($super,'_',substring-before(substring-after(@name,'_'),'_')))[1])]"> 
      <Set name="{substring-before(substring-after(@name,'_'),'_')}"> 
       <xsl:variable name="set" select="concat($super,'_',substring-before(substring-after(@name,'_'),'_'))"/> 
       <xsl:for-each select="//Set/Element[starts-with(@name,$set)]"> 
        <Element name="{substring-after(substring-after(@name,'_'),'_')}"/> 
       </xsl:for-each> 
      </Set> 
      </xsl:for-each> 
      </Superset> 
    </xsl:for-each> 
</xsl:template> 

Der Trick ist nur muenchian grouping und die richtigen Schlüsselwerte zu erfassen.

Es ist wirklich nicht schön, so bin ich sicher, eine bessere Lösung zur Verfügung, es gibt aber ich bin jetlagged: P

+0

+1 - Obwohl ich empfehlen würde, die "// Set/Element" -Ausdrücke durch die entsprechenden Aufrufe von key() zu ersetzen - dafür haben Sie schließlich doch etwas. :) – Tomalak

+0

Danke Jungs für Ihre eingehende Lösung. Da mein Fall viel komplizierter ist als das angegebene Beispiel, habe ich noch nicht entschieden, welche Ihrer Versionen ich verwenden soll. Die Zeit wird sagen, ich nehme an ... – Yaneeve

+0

Ich kann nicht scheinen, dieses xslt-Fragment zu validieren, selbst wenn ich es mit dem xsl: stylesheet -Element verarbeite ... – Yaneeve

Verwandte Themen