2016-11-29 2 views
0

Ich versuche, diese XML-Beispieldatei zu verschachteln. Ich habe versucht, einfache Vorlagen zu verwenden, die den aktuellen Knoten weitergeben.XSLT: Wie nistet man Flat Code?

<?xml version="1.0" encoding="UTF-8"?> 
<chapter> 
    <h1>h1</h1> 
    <p>text1</p> 
    <p>text2</p> 
    <p>text3</p> 
    <h2>h2</h2> 
    <p>text1</p> 
    <p>text2</p> 
    <p>text3</p> 
    <p>text4</p> 
    <p>text5</p> 
    <p>text6</p> 
    <h1>h1</h1> 
    <p>text1</p> 
    <p>text2</p> 
    <p>text3</p> 
    <h2>h2</h2> 
    <h3>h3</h3> 
    <p>text1</p> 
    <p>text2</p> 
    <p>text3</p> 
    <h2>h2</h2> 
    <p>text1</p> 
    <p>text2</p> 
    <p>text3</p> 
</chapter> 

mit folgendem Sheet:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0"> 
    <xsl:template match="/"> 
     <chapter> 
     <xsl:apply-templates/> 
     </chapter> 
    </xsl:template> 

    <xsl:template match="h1"> 
     <level_1> 
      <head> 
       <xsl:value-of select="."/> 
      </head> 
      <xsl:apply-templates select="following::*"/> 
     </level_1> 
    </xsl:template> 

    <xsl:template match="h2"> 
     <head> 
      <xsl:value-of select="."/> 
     </head> 
     <level_2> 
      <head> 
       <xsl:value-of select="."/> 
      </head> 
      <xsl:apply-templates select="following::*"/> 
     </level_2> 
    </xsl:template> 

    <xsl:template match="h3"> 
     <head> 
      <xsl:value-of select="."/> 
     </head> 
     <level_3> 
      <head> 
       <xsl:value-of select="."/> 
      </head> 
      <xsl:apply-templates select="following::*"/> 
     </level_3> 
    </xsl:template> 

    <xsl:template match="p"> 
     <text> 
      <xsl:value-of select="."></xsl:value-of> 
     </text> 
    </xsl:template> 

</xsl:stylesheet> 

Aber das Ergebnis überhaupt nicht funktioniert. following::* funktioniert nicht, weil h1 auch folgt , also wie weiß die Vorlage, wann zu schließen?

sollte die Ausgabe sein:

<chapter> 
<level_1> 
<head>h1</head> 
<text>text1</text> 
<text>text2</text> 
<text>text3</text> 
<level_2> 
<head>h2</head> 
<text>text1</text> 
<text>text2</text> 
<text>text3</text> 
<text>text4</text> 
<text>text5</text> 
<text>text6</text> 
</level_2> 
... 
</level_1> 
</chapter> 

Thanks a lot!

+0

Sie müssen sagen, ob es XSLT 1.0 oder XSLT 2.0. Die Lösung in 2.0 ist viel einfacher, weil '' verfügbar ist. Andere Fragen: (a) Gibt es eine maximale Verschachtelung oder wollen Sie mit beliebiger Tiefe umgehen? (b) Können Sie annehmen, dass die Eingabe "wohlgeformt" ist, z. nein h3 ohne h2? –

+0

Es ist XSLT 2.0. (a) Es gibt keine maximale Verschachtelung (b) Ich kann annehmen, dass die Eingabe gut gebildet ist. – Ferestes

Antwort

1

OK, hier geht.

eine Nutzenfunktion testen definieren, ob ein Element ein hn:

<xsl:function name="f:isLevel" as="xs:boolean"> 
    <xsl:param name="h" as="element(*)"/> 
    <xsl:param name="level" as="xs:integer"/> 
    <xsl:sequence select="name($h) = concat('h', $level)"/> 
</xsl:function> 

eine rekursive Funktion definieren, die die Gruppierung für die Stufe N führt:

<xsl:function name="f:group" as="element(*)*"> 
     <xsl:param name="input" as="element(*)*"/> 
     <xsl:param name="level" as="xs:integer"/> 
     <xsl:for-each-group 
        group-starting-with="*[f:isLevel(., $level)]"> 
     <xsl:choose> 
      <xsl:when test="f:isLevel(., $level)"> 
      <xsl:element name="level_{$level}"> 
       <head><xsl:copy-of select="."/></head> 
       <xsl:sequence select=" 
        f:group(remove(current-group(), 1), $level+1)"/> 
      </xsl:element> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:sequence select="current-group()"/> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:for-each-group> 
    </xsl:function> 

Wie funktionierts? Bei einer Eingabe wie (p, p, h1, p, p, h1, p) entstehen drei Gruppen: (p, p, p), (h1, p, p) und (h1, p). Eine Gruppe, die mit h1 beginnt, nimmt die xsl: when-Verzweigung: Sie erstellt ein levelN-Element mit dem Inhalt des ersten Elements (h1) und dann das Ergebnis der rekursiven Gruppierung auf den Rest der Gruppe nach dem h1-Element.

Die Anfangsgruppe (ohne führende h1) nimmt den xsl: sonst Zweig und wird einfach in den Ausgang kopiert.

Die Rekursion wird beendet, wenn keine Elemente mit dem Namen h {N} vorhanden sind: In diesem Fall erstellt der Befehl for-each-group nur eine einzige Gruppe, die im Zweig xsl: sonst verarbeitet wird und keine weitere Rekursion verursacht.

Um dies laufen Sie so etwas wie

<xsl:template match="body"> 
    <xsl:copy-of select="f:group(*, 1)"/> 
</xsl:template> 
+0

Dieser funktioniert perfekt, vielen Dank! Jetzt habe ich etwas zum arbeiten. – Ferestes

1

Hier wollen werden, ist ein, wie man es aussehen könnte:

XSLT 1,0

<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="h2" match="h2" use="generate-id(preceding-sibling::h1[1])" /> 
<xsl:key name="h3" match="h3" use="generate-id(preceding-sibling::h2[1])" /> 
<xsl:key name="p" match="p" use="generate-id(preceding-sibling::*[starts-with(name(), 'h')][1])" /> 

<xsl:template match="/chapter"> 
    <xsl:copy> 
     <xsl:apply-templates select="h1"/> 
    </xsl:copy> 
</xsl:template>  

<xsl:template match="h1"> 
    <level_1> 
     <head> 
      <xsl:value-of select="."/> 
     </head> 
     <xsl:apply-templates select="key('h2', generate-id()) | key('p', generate-id())"/> 
    </level_1> 
</xsl:template>  

<xsl:template match="h2"> 
    <level_2> 
     <head> 
      <xsl:value-of select="."/> 
     </head> 
     <xsl:apply-templates select="key('h3', generate-id()) | key('p', generate-id())"/> 
    </level_2> 
</xsl:template> 

<xsl:template match="h3"> 
    <level_3> 
     <head> 
      <xsl:value-of select="."/> 
     </head> 
     <xsl:apply-templates select="key('p', generate-id())"/> 
    </level_3> 
</xsl:template>  

<xsl:template match="p"> 
    <text> 
     <xsl:value-of select="."/> 
    </text> 
</xsl:template> 

</xsl:stylesheet>