2017-10-16 2 views
0

Ich habe die folgende XML:Anruf benannte Vorlage für die nächste Geschwister

<Text> 
     <p id="258">Step.</p> 
     <p id="1123">Step info.</p> 
     <p id="258">Step.</p> 
     <p id="1123">Step info.</p> 
     <p id="258">Step.</p> 
     <p id="1123">Step info:</p> 
     <p id="1123">- Comment.</p> 
     <p id="1123">- Comment.</p> 
     <p id="1123">- Comment.</p> 
    </Text> 

ich es in ein DocBook drehen muss <orderedlist>:

<orderedlist> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step info.</emphasis> 
      </para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step info.</emphasis> 
      </para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step info:</emphasis> 
      </para> 
      <para> 
       <emphasis>- Comment:</emphasis> 
      </para> 
      <para> 
       <emphasis>- Comment:</emphasis> 
      </para> 
      <para> 
       <emphasis>- Comment:</emphasis> 
      </para> 
     </listitem> 
    </orderedlist> 

ich zuerst schalten Sie alle <p id="258"> Elemente in <listitem><para>:

<xsl:template match="AIT:p[@id='258'][1]"> 
    <orderedlist> 
     <xsl:for-each select="../AIT:p[@id='258']"> 
      <xsl:call-template name="stepNoLine"/> 
     </xsl:for-each> 
    </orderedlist> 
</xsl:template> 

<xsl:template name="stepNoLine"> 
    <listitem> 
     <para> 
      <xsl:apply-templates select="*|node()"/> 
     </para> 
    </listitem> 
</xsl:template> 

Und ich lösche alle nicht-ersten Elemente:

<xsl:template match="AIT:p[@id='258'][position() > 1]"/> 

So weit so gut:

<orderedlist> 
     <listitem> 
      <para>Step.</para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
     </listitem> 
    </orderedlist> 

Aber jetzt weiß ich nicht, wie von <p id="1123"> Elemente kümmern. Alle <p id="1123"> zwischen zwei <p id="258"> müssen Geschwister der ersten <p id="258"> und Kinder von <listitem> sein. Nochmal:

<listitem> 
     <para>Step.</para> 
     <para> 
      <emphasis>Step info.</emphasis> 
     </para> 
    </listitem> 

Mein kümmerlicher Versuch fehlschlägt schändlich in Unehre: ist

<orderedlist> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step.</emphasis> 
      </para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step.</emphasis> 
      </para> 
     </listitem> 
     <listitem> 
      <para>Step.</para> 
      <para> 
       <emphasis>Step.</emphasis> 
      </para> 
     </listitem> 
    </orderedlist> 

Mit anderen Worten, jedes <p id="258"> Element zweimal kopiert:

<xsl:template name="stepNoLine"> 
    <listitem> 
     <para> 
      <xsl:apply-templates select="*|node()"/> 
     </para> 
     <xsl:if test="following-sibling::AIT:p/@id='1123'"> 
      <xsl:call-template name="stepInfo"/> 
     </xsl:if> 
    </listitem> 
</xsl:template> 

<xsl:template name="stepInfo"> 
    <para> 
     <emphasis> 
      <xsl:apply-templates select="*|node()"/> 
     </emphasis> 
    </para> 
</xsl:template> 

ich so etwas bekommen. Ich dachte, die <xsl:if> machte das nächste Geschwister den aktuellen Knoten, aber ich war offensichtlich falsch.

Andere Versuche (wie mit einem xsl:for-each anstelle von xsl:if) scheiterten auf ebenso miserable Weise.

Kann mir bitte jemand in die richtige Richtung zeigen?

+1

können Sie mit XSLT-2.0? –

+0

Ich benutze msxsl, aber laut Microsoft ist das nur XSLT 1.0. Ich sehe Saxon-HE 9.8 implementiert XSLT 3.0 - würde das tun? –

Antwort

1

XSLT 2.0 oder 3.0 können Sie verwenden for-each-group group-starting-with:

<xsl:template match="Text"> 
    <orderedlist> 
     <xsl:for-each-group select="*" group-starting-with="p[@id = 258]"> 
      <listitem> 
       <xsl:apply-templates select="current-group()"/> 
      </listitem> 
     </xsl:for-each-group> 
    </orderedlist> 
</xsl:template> 

<xsl:template match="Text/p[@id = 258]"> 
    <para> 
     <xsl:apply-templates/> 
    </para> 
</xsl:template> 

<xsl:template match="Text/*[not(self::p[@id = 258])]"> 
    <para> 
     <emphasis> 
      <xsl:apply-templates/> 
     </emphasis> 
    </para> 
</xsl:template> 
+0

danke, die Vorlagen funktionieren wie beabsichtigt (obwohl ich nicht sicher bin, verstehe ich, wie sie es tun). –

1

Heute ist die richtige Antwort auf fast jedes Problem dieser Art „Verwendung XSLT 2.0“ sein wird, aber in Umgebungen, in denen XSLT nur 1.0 verfügbar Es ist nützlich, das Problem auch in 1.0 lösen zu können.

die folgende Eingabe Betrachten isomorph zum Beispiel gezeigt, jedoch mit unterschiedlichen Zeichendaten, um es einfacher zu sehen, was passiert:

<Text> 
    <p id="258">First step.</p> 
    <p id="1123">Step info for step 1.</p> 
    <p id="258">Step two.</p> 
    <p id="1123">Step info for second step.</p> 
    <p id="258">Step three.</p> 
    <p id="1123">Step info for third step:</p> 
    <p id="1123">- Comment on step 3.</p> 
    <p id="1123">- Comment 2 on step 3.</p> 
    <p id="1123">- Comment 3 on step 3.</p> 
</Text> 

Mit diesem Eingang, dann ist es eine wenig leichter zu sehen, dass (a) Ihr Entwurf Stylesheet (vorausgesetzt, ich habe es richtig aus Ihrer Beschreibung rekonstruiert) schafft es, einen listItem für jeden Schritt zu bekommen (trotz der etwas Round-the-houses-Methoden, die es verwendet), aber auch, dass (b) es keine Vorlagen hat das entspricht entweder dem Element oder einem Element mit id="1123", was bedeutet, dass die Standardvorlagen ausgelöst werden und wir einen Dump von ihnen erhalten Die Zeichendaten von 1123 Absätzen folgen der Ausgabe aus der Vorlage für den ersten 258 Absatz.

<orderedlist> 
<listitem> 
    <para>First step.</para> 
    <para><emphasis>First step.</emphasis></para> 
</listitem> 
<listitem> 
    <para>Step two.</para> 
    <para><emphasis>Step two.</emphasis></para> 
</listitem> 
<listitem> 
    <para>Step three.</para> 
    <para><emphasis>Step three.</emphasis></para> 
</listitem> 
</orderedlist> 
     Step info for step 1. 

     Step info for second step. 

     Step info for third step: 
     - Comment on step 3. 
     - Comment 2 on step 3. 
     - Comment 3 on step 3. 

Das unmittelbare Problem in dem Code ist, dass stepNoLine Anrufe stepinfo ohne etwas zu tun den Kontextknoten zu ändern, so dass die Vorlage Anwendung dort die Kinder der 258 Absatz verarbeitet, nicht die folgenden 1123 Absätze.

Die Eingabe, die Sie verarbeiten, enthält viele Informationen, die in die Sequenz der Elemente eingebettet sind. Ihr Pull-Style-Stylesheet ignoriert diese Informationen und versucht, sie aus dem Nichts neu zu erstellen. Ihre Stylesheets werden einfacher und funktionieren besser, wenn Sie sich von der Eingabe leiten lassen, die in der XSLT-Programmierung normalerweise als Push-Stil bezeichnet wird.

Im folgenden XSLT-Stylesheet, die Vorlage für Text ruft xsl: apply-templates nur für die Kinder, die listItem Elemente erzeugen soll, das heißt p Elemente mit id="258". Die 1123 Absätze werden von dieser Anweisung nicht verarbeitet.

Die Vorlagen für die 258 Absatzelemente wiederum erstellen den gesamten Inhalt des Listenelements: zuerst den Absatz 258 und dann die Folge aller folgenden Elemente p mit id="1123". (Wir wenden Vorlagen nur auf die erste an, aber die erste kümmert sich um die gesamte Sequenz.)

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

    <xsl:template match="Text"> 
    <xsl:element name="orderedList"> 
     <xsl:apply-templates select="p[@id='258']"/> 
    </xsl:element> 
    </xsl:template> 

    <xsl:template match="p[@id='258']"> 
    <xsl:element name="listitem"> 
     <xsl:element name="para"> 
     <xsl:apply-templates/> 
     </xsl:element> 
     <xsl:apply-templates select="following-sibling::*[1]/self::p[@id='1123']"/> 
    </xsl:element> 
    </xsl:template> 

    <xsl:template match="p[@id='1123']"> 
    <xsl:element name="para"> 
     <xsl:apply-templates/> 
    </xsl:element> 
    <xsl:apply-templates select="following-sibling::*[1]/self::p[@id='1123']"/> 
    </xsl:template> 

</xsl:stylesheet> 

N.B. In der Vorlage für 258 Elements versuchen wir nicht, Vorlagen auf alle entsprechenden Geschwisterelemente mit id="1123" anzuwenden. Wir könnten und alles wäre einfacher, wenn wir in XPath 1.0 "alle folgenden p Elemente mit id="1123" bis einschließlich, aber nicht die erste p mit id="258", oder das Ende des übergeordneten Elements" und wusste mit Sicherheit, dass wir es richtig gemacht hatten. Es ist einfacher zu sagen, "nehmen Sie sofort das folgende Geschwister, wenn und nur wenn es eine mit id="1123" ist, und dann die Vorlage für dieses Element das gleiche tun. Wenn wir eine mit id="1123" erreichen, deren unmittelbar folgende Geschwister ist kein p mit . id="1123", oder die keine folgende Geschwister hat, stoppt die Rekursion

Aus dem modifizierten Eingangs erzeugt diese als Ausgabe:

<?xml version="1.0" encoding="UTF-8"?> 
<orderedList> 
    <listitem> 
    <para>First step.</para> 
    <para>Step info for step 1.</para> 
    </listitem> 
    <listitem> 
    <para>Step two.</para> 
    <para>Step info for second step.</para> 
    </listitem> 
    <listitem> 
    <para>Step three.</para> 
    <para>Step info for third step:</para> 
    <para>- Comment on step 3.</para> 
    <para>- Comment 2 on step 3.</para> 
    <para>- Comment 3 on step 3.</para> 
    </listitem> 
</orderedList> 

, die ich ist glauben, was erforderlich ist (Wenn Sie die 1123 wickeln möchten. Absätze Inhalt in emph, sollte es leicht zu sehen, wo zu tun Hut.)

+0

@ CMS-McQ, was soll ich sagen, ich verneige mich vor deiner Meisterschaft. Vielen Dank. –

1

<xsl:template match="Text"> 
    <xsl:element name="orderedlist"> 
     <xsl:apply-templates select="p[@id='258']"/> 
    </xsl:element> 
</xsl:template> 

<xsl:template match="p"> 
    <xsl:choose> 
     <xsl:when test="@id='258'"> 
      <xsl:element name="listitem"> 
       <xsl:element name="para"><xsl:apply-templates /></xsl:element> 
       <xsl:apply-templates select="following-sibling::p[1][@id = '1123']"/> 
      </xsl:element> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:element name="para"> 
       <xsl:element name="emphasis"> 
        <xsl:apply-templates /> 
       </xsl:element> 
      </xsl:element> 
      <xsl:apply-templates select="following-sibling::p[1][@id = '1123']"/> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

+0

Alles, danke für die Antworten. Ich erkannte jedoch, dass meine Frage nicht sehr präzise war. Alle Elemente aus meinem Beispiel sollen Kinder desselben sein, aber ein Element kann auch verschiedene Listen oder verschachtelte Listen enthalten. Es ist nicht immer der Fall, dass wird wie in allen beigetragen . Sorry für den Mangel an Klarheit. Alle Antworten lösen das angegebene Problem, aber ich habe das Problem nicht gut beschrieben. Ich stimme zu, dass XSLT 2.0 nicht immer die Antwort ist, aber ich schaue (h/t @Martin) mit der Gruppe angrenzend. –

Verwandte Themen