2009-03-19 5 views
3

Ich bin eine große Wortdatei mit Test-Beschreibungen Parsen und habe ein Problem des Umfangs der Knoten. Word erstellt im Grunde eine Liste von Absätzen, und ich möchte sie in einen übergeordneten Knoten gruppieren. Also für jeden Knoten 'A' möchte ich alle folgenden Knoten bis zum nächsten Knoten 'A' in 'A' gruppieren.Group Liste der Knoten in Baum von Knoten mit XSL

Wie kann dies mit XSL getan werden?

Beispiel: Ich habe gelernt:

<A/> 
<ab/> 
<ac/> 
<A/> 
<ab/> 
<ac/> 

Aber Notwendigkeit:

<A> 
<ab/> 
<ac/> 
</A> 
<A> 
<ab/> 
<ac/> 
</A> 

Vielen Dank!

+0

Sie haben noch konkreter werden, was besondere macht? – Diadistis

+0

@Hugo Dies ist eine Lösung für das beschriebene Problem, und es erzeugt das gewünschte Ergebnis. Falls Sie ein anderes Problem haben, posten Sie es bitte, damit es gelöst werden kann. Sie sollten keine Probleme haben, diese Lösung auf das aktuelle Problem anzuwenden - es produziert nur das gewünschte Ergebnis –

Antwort

3

Wenn Sie meinen, alle Knoten folgende <A> übereinstimmen, aber kommen, bevor die nächste <A>, ich glaube, Sie so etwas wie diese verwenden:

<xsl:template match="A"> 
    <xsl:copy> 
    <!-- start of range --> 
    <xsl:variable name="start" select="count(preceding-sibling::*) + 1" /> 
    <!-- end of range --> 
    <xsl:variable name="stop"> 
     <xsl:choose> 
     <!-- either just before the next A node --> 
     <xsl:when test="following-sibling::A"> 
      <xsl:value-of select="count(following-sibling::A[1]/preceding-sibling::*) + 1" /> 
     </xsl:when> 
     <!-- or all the rest --> 
     <xsl:otherwise> 
      <xsl:value-of select="count(../*) + 1" /> 
     </xsl:otherwise> 
     </xsl:choose> 
    </xsl:variable> 

    <!-- this for debugging only --> 
    <xsl:attribute name="range"> 
     <xsl:value-of select="concat($start + 1, '-', $stop - 1)" /> 
    </xsl:attribute> 

    <!-- copy all nodes in the calculated range --> 
    <xsl:for-each select="../*[position() &gt; $start and position() &lt; $stop]"> 
     <xsl:copy-of select="." /> 
    </xsl:for-each> 
    </xsl:copy> 
</xsl:template> 

Für Ihre Eingabe:

<root> 
    <A /> 
    <ab /> 
    <ac /> 
    <A /> 
    <ab /> 
    <ac /> 
</root> 

Ich bekomme (ich ließ das Attribut "Bereich" in, um die Berechnungen sichtbar zu machen):

<A range="2-3"> 
    <ab /> 
    <ac /> 
</A> 
<A range="5-6"> 
    <ab /> 
    <ac /> 
</A> 
+0

Es gibt wahrscheinlich einen schöneren Weg, es zu tun. Ich bin gespannt, welche Lösungen andere Leute finden. – Tomalak

+0

Die Verwendung von Schlüsseln ist in der Regel effizienter und bietet kompaktere Lösungen. Siehe meine Antwort. –

+0

Ich endete mit einer zweiphasigen Variante dieser Antwort. Unter Verwendung der vorhergehenden Geschwisterachse würde jeder Kindknoten ein Attribut "gehört zu" erhalten, und dann würde ich sie in einem zweiten Schritt zusammenführen. Vielen Dank! – Hugo

4

Es gibt eine einfache und sehr leistungsfähige Lösung mit Schlüsseln.

Diese Transformation:

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

<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:key name="kFollowing" match="*[not(self::A)]" 
    use="generate-id(preceding-sibling::A[1])"/> 

    <xsl:template match="/*"> 
    <t> 
     <xsl:apply-templates select="A"/> 
    </t> 
    </xsl:template> 

    <xsl:template match="A"> 
    <A> 
     <xsl:copy-of select= 
      "key('kFollowing',generate-id())"/> 
    </A> 
    </xsl:template> 
</xsl:stylesheet> 

wenn sie auf dem ursprünglichen XML-Dokument angewendet:

das gewünschte Ergebnis produziert:

<t> 
    <A> 
     <ab/> 
     <ac/> 
    </A> 
    <A> 
     <ab/> 
     <ac/> 
    </A> 
</t> 

Bitte notieren Sie, wie die Definition der <xsl:key>, kombiniert mit der Verwendung der key() Funktion macht die einfachste und natürliche Erfassung aller Geschwisterelemente zwischen zwei benachbarten <A/> Elemente.

+0

Danke für die Antwort. Entschuldigung, dass ich nicht früher geantwortet habe, musste es in die Warteschleife legen. Dies war die eleganteste Lösung, aber ich konnte es fast nur zum Laufen bringen. Das As würde alle ab/ac vom aktuellen Knoten und vorwärts enthalten. Vielen Dank! – Hugo

+0

@Hugo Dies ist eine Lösung für das beschriebene Problem, und es erzeugt das gewünschte Ergebnis. Falls Sie ein anderes Problem haben, posten Sie es bitte, damit es gelöst werden kann. Sie sollten kein Problem damit haben, diese Lösung auf das aktuelle Problem anzuwenden - es erzeugt nur das gewünschte Ergebnis. –

3

XSLT 2.0-Lösung:

<xsl:for-each-group select="*" group-starting-with="A"> 
    <xsl:element name="{name(current-group()[1])}"> 
    <xsl:copy-of select="current-group()[position() gt 1]"/> 
    </xsl:element> 
</xsl:for-each-group> 
+0

Sehr hübsch! Kann t wegen Ant nicht verwenden. Aber danke, habe eine Erwiderung bekommen. – Hugo

+0

Ausgezeichnet. Und natürlich kann man das mit Ant nutzen: http://stackoverflow.com/questions/919692/how-to-execute-xslt-2-0-with-ant –