3 votes

XSLT - Utilisation de substrats avec copy-of pour préserver les balises HTML internes

J'ai quelques XML de ce type :

<story><p><strong>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</strong>Nulla vel mauris metus. Etiam vel tortor vel magna bibendum euismod nec varius turpis. Nullam ullamcorper, nunc vel auctor consectetur, quam felis accumsan eros, lacinia fringilla mauris est vel lectus. Curabitur et tortor eros. Duis sed convallis metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras tempus quam sed enim gravida bibendum. Vestibulum magna ligula, varius in sodales eu, ultricies volutpat sem. Phasellus ante justo, vestibulum eu hendrerit a, posuere vitae est. Integer at pulvinar est.</p><p>Quisque a commodo eros. Integer tempus mi sit amet leo consectetur adipiscing. Nullam sit amet enim metus. Curabitur sollicitudin egestas arcu, at convallis enim iaculis eget. Etiam faucibus, justo sit amet lacinia consectetur, purus nunc rhoncus dui, id malesuada tortor est sed orci. Quisque eget nisi vitae mi facilisis varius. Integer fringilla eros sit amet velit vehicula commodo. </p><br /><span>And some more text here</span> </story>

Je veux le faire :

<xsl:copy-of select="substring(story/node(),1,500)"/>

Voici le problème. Je perds le <p>, <strong>, <br /> et d'autres balises HTML à l'intérieur du <story> à chaque fois que je prends la sous-chaîne. Existe-t-il un moyen de récupérer les 500 premiers caractères de la balise story tout en conservant les balises HTML internes ?

Merci de votre attention !

3voto

Jukka Matilainen Points 4019

Voici une autre approche dans XSLT 1.0, sans avoir à utiliser l'option node-set extension :

  <xsl:template match="@*|node()" mode="limit-length">
    <xsl:param name="length"/>
    <xsl:copy>
      <xsl:apply-templates select="@*" mode="limit-length"/>
      <xsl:call-template name="copy-nodes">
        <xsl:with-param name="nodes" select="node()"/>
        <xsl:with-param name="length" select="$length"/>
      </xsl:call-template>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="text()" mode="limit-length">
    <xsl:param name="length"/>
    <xsl:value-of select="substring(., 1, $length)"/>
  </xsl:template>

  <xsl:template name="copy-nodes">
    <xsl:param name="nodes"/>
    <xsl:param name="length"/>
    <xsl:if test="$length &gt; 0 and $nodes">
      <xsl:variable name="head" select="$nodes[1]"/>
      <xsl:apply-templates select="$head" mode="limit-length">
        <xsl:with-param name="length" select="$length"/>
      </xsl:apply-templates>
      <xsl:variable name="remaining" select="$length - string-length($head)"/>
      <xsl:if test="$remaining &gt; 0 and count($nodes) &gt; 1">
        <xsl:call-template name="copy-nodes">
          <xsl:with-param name="nodes" select="$nodes[position() &gt; 1]"/>
          <xsl:with-param name="length" select="$remaining"/>
        </xsl:call-template>
      </xsl:if>
    </xsl:if>
  </xsl:template>

Il s'agit essentiellement du modèle d'identité, la copie des nœuds enfants étant transférée à un modèle récursif qui se charge de respecter la longueur maximale de la chaîne, ainsi qu'un modèle distinct pour les nœuds de texte, qui les tronque à la longueur maximale.

Vous pouvez l'invoquer pour l'exemple d'entrée comme suit :

<xsl:call-template name="copy-nodes">
  <xsl:with-param name="nodes" select="story/node()"/>
  <xsl:with-param name="length" select="500"/>
</xsl:call-template>

Suivi : diviser l'histoire

Pour ce qui est de diviser l'histoire en deux parties après la première coupure ou fin de paragraphe après N caractères, je vais aller de l'avant et faire l'hypothèse simplificatrice que vous voulez envisager de diviser l'histoire seulement après que <p> y <br> qui apparaissent comme des enfants directs sous l'élément <story> (et non pas imbriqués à une profondeur arbitraire). Cela facilite grandement la résolution du problème.

Voici une façon d'y parvenir : Pour obtenir le contenu de la première partie, vous pouvez utiliser un modèle qui traitera un ensemble de nœuds frères jusqu'à ce que la longueur maximale de la chaîne soit dépassée et qu'un message d'erreur br o p est rencontrée, puis s'arrêter.

  <xsl:template match="node()" mode="before-break">
    <xsl:param name="length"/>
    <xsl:if test="$length &gt; 0 or not(self::br or self::p)">
      <xsl:copy-of select="."/>
      <xsl:apply-templates select="following-sibling::node()[1]"
                           mode="before-break">
        <xsl:with-param name="length" select="$length - string-length(.)"/>
      </xsl:apply-templates>
    </xsl:if>
  </xsl:template>

Pour la deuxième partie, vous pouvez créer un autre modèle qui recherche la même condition que le modèle précédent, mais qui ne produit rien jusqu'à ce moment-là :

  <xsl:template match="node()" mode="after-break">
    <xsl:param name="length"/>
    <xsl:choose>
      <xsl:when test="$length &gt; 0 or not(self::br or self::p)">
        <xsl:apply-templates select="following-sibling::node()[1]"
                             mode="after-break">
          <xsl:with-param name="length" select="$length - string-length(.)"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:otherwise>
        <xsl:if test="not(self::br)"> <!-- suppress the <br/> -->
          <xsl:copy-of select="."/>
        </xsl:if>
        <xsl:copy-of select="following-sibling::node()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

Voici comment vous pouvez utiliser ces modèles pour diviser une histoire en deux <div> s.

  <xsl:template match="story">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <div>
        <xsl:apply-templates select="node()[1]" mode="before-break">
          <xsl:with-param name="length" select="500"/>
        </xsl:apply-templates>
      </div>
      <div>
        <xsl:apply-templates select="node()[1]" mode="after-break">
          <xsl:with-param name="length" select="500"/>
        </xsl:apply-templates>
      </div>
    </xsl:copy>
  </xsl:template>

0voto

Tim C Points 25946

Une question très similaire est posée à l'adresse suivante Obtenir un texte d'introduction de N caractères avec XSLT 1.0 à partir de XHTML

Voici le XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
   <xsl:param name="MAXCHARS">500</xsl:param>

   <xsl:template match="/body">
      <xsl:apply-templates select="child::node()"/>
   </xsl:template>

   <xsl:template match="node()">
      <xsl:param name="LengthToParent">0</xsl:param>

      <!-- Get length of previous siblings -->
      <xsl:variable name="previousSizes">
         <xsl:for-each select="preceding-sibling::node()">
            <length>
               <xsl:value-of select="string-length(.)"/>
            </length>
         </xsl:for-each>
      </xsl:variable>
      <xsl:variable name="LengthToNode" select="sum(msxsl:node-set($previousSizes)/length)"/>

      <!-- Total amount of characters processed so far -->
      <xsl:variable name="LengthSoFar" select="$LengthToNode + number($LengthToParent)"/>

      <!-- Check limit is not exceeded -->
      <xsl:if test="$LengthSoFar &lt; number($MAXCHARS)">
         <xsl:choose>
            <xsl:when test="self::text()">
               <!-- Output text nonde with ... if required -->
               <xsl:value-of select="substring(., 1, number($MAXCHARS) - $LengthSoFar)"/>
               <xsl:if test="string-length(.) &gt; number($MAXCHARS) - $LengthSoFar">...</xsl:if>
            </xsl:when>
            <xsl:otherwise>
               <!-- Output copy of node and recursively call template on its children -->
               <xsl:copy>
                  <xsl:copy-of select="@*"/>
                  <xsl:apply-templates select="child::node()">
                     <xsl:with-param name="LengthToParent" select="$LengthSoFar"/>
                  </xsl:apply-templates>
               </xsl:copy>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:if>
   </xsl:template>

</xsl:stylesheet>

Il fonctionne en parcourant les nœuds enfants d'un nœud et en totalisant la longueur des frères et sœurs précédents jusqu'à ce point. Notez que le code pour obtenir la longueur des frères et sœurs précédents nécessite l'utilisation de la fonction node-set, qui est une fonction d'extension de XSLT 1.0. Dans mon exemple, j'utilise la fonction Microsoft Extension.

Lorsqu'un nœud n'est pas un nœud de texte, la longueur totale des caractères jusqu'à ce point sera la somme des longueurs des frères et sœurs précédents, plus la somme des frères et sœurs précédents du nœud parent (qui est transmis en tant que paramètre au modèle).

Lorsque la XSLT est appliquée à votre XML d'entrée, le résultat est le suivant :

<story>
    <p>
        <strong>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</strong>Nulla vel mauris metus. Etiam vel tortor vel magna bibendum euismod nec varius turpis. Nullam ullamcorper, nunc vel auctor consectetur, quam felis accumsan eros, lacinia fringilla mauris est vel lectus. Curabitur et tortor eros. Duis sed convallis metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras tempus quam sed enim gravida bibendum. Vestibulum magna ligula, varius in sodales eu, ultr...
    </p>
</story>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X