2017-10-16 4 views
0

prochain j'ai le code XML suivant:Appel de modèle nommé pour frères et soeurs

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

je dois en faire un DocBook <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> 

Je premier tour tous <p id="258"> éléments en <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> 

Et je supprime tous les éléments non-premiers:

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

So far so good:

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

Mais maintenant, je ne sais pas comment prendre soin de <p id="1123"> éléments. Tous <p id="1123"> entre deux <p id="258"> doivent être les frères et soeurs du premier <p id="258">, et les enfants de <listitem>. Encore une fois:

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

Ma chétif tentative échoue honteusement méprisable:

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

je reçois quelque chose comme:

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

En d'autres termes, chaque élément <p id="258"> est copié deux fois. Je pensais que le <xsl:if> faisait du prochain frère le nœud actuel, mais j'étais évidemment dans l'erreur.

D'autres tentatives (comme l'utilisation d'un xsl:for-each au lieu de xsl:if) ont échoué de manière tout aussi misérable.

Est-ce que quelqu'un peut me diriger dans la bonne direction?

+1

pouvez-vous utiliser XSLT 2.0? –

+0

J'utilise msxsl, mais selon Microsoft c'est XSLT 1.0 seulement. Je vois Saxon-HE 9.8 implémente XSLt 3.0 - cela ferait-il? –

Répondre

1

XSLT 2.0 ou 3.0, vous pouvez utiliser 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

merci, les modèles fonctionnent comme prévu (même si je ne suis pas sûr de comprendre comment ils le font). –

1

De nos jours, la bonne réponse à presque tous les problèmes de ce genre va être « utiliser XSLT 2.0 », mais dans des environnements où que XSLT 1.0 est disponible il est également utile de pouvoir résoudre le problème dans la version 1.0.

Tenir compte de l'entrée suivante, isomorphe à l'exemple représenté, mais avec des données de caractères différents pour le rendre plus facile de voir ce qui se passe:

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

Avec cette entrée, il est un peu plus facile de voir que (a) votre feuille de style de brouillon (en supposant que je l'ai reconstruit correctement à partir de votre description) réussit à obtenir un listItem pour chaque étape (malgré les méthodes légèrement rondes qu'il utilise pour le faire), mais aussi (b) il n'a pas de modèles qui correspondent soit à l'élément Text, soit aux éléments p avec id="1123", ce qui signifie que les modèles par défaut se déclenchent et que 1123 paragraphes 'données de caractère suivant la sortie du modèle pour le premier paragraphe 258.

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

Le problème immédiat dans le code est que stepNoLine appels stepinfo sans rien faire pour changer le nœud de contexte, de sorte que l'application du modèle, il traite les enfants du 258 paragraphe, et non les paragraphes suivants 1123.

L'entrée que vous traitez contient beaucoup d'informations intégrées dans la séquence d'éléments; votre feuille de style tirée ignore cette information et essaie de la recréer à partir de rien. Vos feuilles de style seront plus simples et feront un meilleur travail si vous les laissez guider par l'entrée, dans ce qui est généralement connu dans la programmation XSLT comme un style «push».

Dans la feuille de style XSLT suivant, le modèle de Text appelle xsl: apply-templates seulement pour les enfants qui doivent produire des éléments listItem, à savoir p éléments avec id="258". Les paragraphes 1123 ne sont pas traités par cette instruction.

Les modèles des éléments de paragraphe 258, à leur tour, créent tout le contenu de l'élément de liste: d'abord le paragraphe 258, puis la séquence de tous les éléments suivants p avec . (Nous appliquons des modèles uniquement à la première, mais le premier se charge de toute la séquence.)

<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. Dans le modèle pour 258 elemetns, nous n'essayons pas d'appliquer des modèles à tous les éléments frères appropriés avec id="1123". Nous pourrions, et tout serait plus simple, si nous avions un moyen pratique de dire, dans XPath 1.0, "tous les éléments immédiatement suivants p avec id="1123" jusqu'à p avec p ou avec la fin de l'élément parent" et sachez avec certitude que nous avions bien compris. Il est plus simple de dire "prendre le frère suivant immédiatement, si et seulement si c'est un p avec id="1123", et que le gabarit pour cet élément fasse la même chose quand nous atteignons un p avec id="1123" dont le frère suivant n'est pas un p avec . id="1123", ou qui n'a pas frères et soeurs qui suit, la récursion arrête

de l'entrée modifiée, ce produit en sortie:

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

que je crois est ce qui est nécessaire (Si vous voulez envelopper le 1123. paragraphes 'contenu dans emph, il devrait être facile de voir où faire chapeau.)

+0

@ CMS-McQ, que puis-je dire, je m'incline devant votre maîtrise. Merci beaucoup. –

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

Tout, merci pour les réponses. Cependant, j'ai réalisé que ma question n'était pas très précise. Tous les éléments de mon exemple sont destinés à être des enfants du même , mais un élément peut également contenir différentes listes ou listes imbriquées. Ce n'est pas toujours le cas devient comme dans tout . Désolé pour le manque de clarté. Toutes les réponses résolvent le problème énoncé, mais je n'ai pas bien énoncé le problème. Je suis d'accord que XSLT 2.0 n'est pas toujours la réponse, mais je regarde dans (h/t @ Martin) avec le groupe adjacent. –