Question

J'effectue une recherche et un remplacement sur le caractère de saut de ligne (
) et en le remplaçant par les balises de fermeture de paragraphe et d'ouverture de paragraphe en utilisant le code suivant :

<xsl:template match="/STORIES/STORY">   
    <component>
        <xsl:if test="boolean(ARTICLEBODY)">
            <p>
                <xsl:call-template name="replace-text">
                        <xsl:with-param name="text" select="ARTICLEBODY"  />
                        <xsl:with-param name="replace" select="'&#10;'" />
                        <xsl:with-param name="by" select="'&lt;/p&gt;&lt;p&gt;'" />
                </xsl:call-template>
            </p>
        </xsl:if>
    </component>
</xsl:template>

<xsl:template name="replace-text">
   <xsl:param name="text"/>
   <xsl:param name="replace" />
   <xsl:param name="by"  />

   <xsl:choose>
   <xsl:when test="contains($text, $replace)">
      <xsl:value-of select="substring-before($text, $replace)"/>
      <xsl:value-of select="$by" disable-output-escaping="yes"/>
      <xsl:call-template name="replace-text">
         <xsl:with-param name="text" select="substring-after($text, $replace)"/>
         <xsl:with-param name="replace" select="$replace" />
         <xsl:with-param name="by" select="$by" />
      </xsl:call-template>
   </xsl:when>
   <xsl:otherwise>
      <xsl:value-of select="$text"/>
   </xsl:otherwise>
   </xsl:choose>
</xsl:template>

Cela fonctionne presque parfaitement, sauf que j'en ai vraiment besoin pour dédoublonner les sauts de ligne car les paragraphes ont tendance à être séparés par 2 ou plus, ce qui entraîne </p><p></p><p>.

Est-il possible de faire en sorte qu'il ne le remplace qu'une fois par paragraphe ?

Était-ce utile?

La solution

disable-output-escaping n'est pas mauvais en soi, mais il n'y a que quelques cas où vous devriez l'utiliser et celui-ci n'en fait pas partie.En XSLT, vous travaillez avec des arbres, pas avec des chaînes de balisage.Voici une solution XSTL 1.0 :

<xsl:template match="/STORIES/STORY">
  <component>
    <xsl:if test="ARTICLEBODY">
      <xsl:call-template name="wrap-text">
        <xsl:with-param name="text" select="ARTICLEBODY"/>
        <xsl:with-param name="delimiter" select="'&#10;'"/>
        <xsl:with-param name="element" select="'p'"/>
      </xsl:call-template>
    </xsl:if>
  </component>
</xsl:template>

<xsl:template name="wrap-text">
  <xsl:param name="text"/>
  <xsl:param name="delimiter"/>
  <xsl:param name="element"/>

  <xsl:choose>
    <xsl:when test="contains($text, $delimiter)">
      <xsl:variable name="t" select="substring-before($text, $delimiter)"/>
      <xsl:if test="normalize-space($t)">
        <xsl:element name="{$element}">
        <xsl:value-of select="$t"/>  
      </xsl:element>
      </xsl:if>        
      <xsl:call-template name="wrap-text">
        <xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
        <xsl:with-param name="delimiter" select="$delimiter"/>
        <xsl:with-param name="element" select="$element"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:if test="normalize-space($text)">
        <xsl:element name="{$element}">
          <xsl:value-of select="$text"/>  
        </xsl:element>
      </xsl:if>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Autres conseils

Essayez ceci (XSLT 2.0) :

    <xsl:template match="/STORIES/STORY">
        <component>
            <xsl:if test="boolean(ARTICLEBODY)">
                <xsl:call-template name="insert_paras">
                    <xsl:with-param name="text" select="ARTICLEBODY/text()"/>
                </xsl:call-template>
            </xsl:if>
        </component>
    </xsl:template>

    <xsl:template name="insert_paras">
        <xsl:param name="text" />

        <xsl:variable name="regex">
            <xsl:text>&#10;(&#10;|\s)*</xsl:text>
        </xsl:variable>
        <xsl:variable name="tokenized-text" select="tokenize($text, $regex)"/>

        <xsl:for-each select="$tokenized-text">
            <p>
                <xsl:value-of select="."/>
            </p>
        </xsl:for-each>
    </xsl:template>

C'est généralement une mauvaise idée d'utiliser des chaînes littérales pour insérer un balisage XML, car vous ne pouvez pas garantir que les résultats sont équilibrés.

Compte tenu des fonctions XPath que vous appelez et dont je ne me souviens pas avoir eu le luxe dans mon travail MSXSL, il semble que vous utilisez un processeur compatible XPath 2.

Si tel est le cas, XPath 2 n'a-t-il pas une fonction de remplacement (chaîne, motif, remplacement) qui prend une expression régulière comme deuxième paramètre ?

<xsl:value-of 
    select="replace(string(.), '&#10;(\s|&#10;)*', '&lt;/p&gt;&lt;p&gt;')" />

Il peut être utile d'avoir un exemple d'entrée XML et de savoir quel processeur vous prévoyez d'utiliser.

D'après votre exemple d'origine, il semble que les paragraphes en double aient tous un préfixe composé uniquement d'espaces blancs.Donc, quelque chose comme cette légère modification pourrait réduire les dupes.

<xsl:when test="contains($text, $replace)">
  <xsl:variable name="prefix" select="substring-before($text, $replace)" />
  <xsl:choose>
    <xsl:when test="normalize-string($prefix)!=''">
      <xsl:value-of select="$prefix"/>
      <xsl:value-of select="$by" disable-output-escaping="yes"/>
    </xsl:when>
  </xsl:choose>
  <xsl:call-template name="replace-text">
     <xsl:with-param name="text" select="substring-after($text, $replace)"/>
     <xsl:with-param name="replace" select="$replace" />
     <xsl:with-param name="by" select="$by" />
  </xsl:call-template>

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top