Pregunta
Estoy realizando una búsqueda y reemplazo en el carácter de avance de línea (
) y reemplazándolo con las etiquetas de cierre de párrafo y apertura de párrafo usando el siguiente código:
<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="' '" />
<xsl:with-param name="by" select="'</p><p>'" />
</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>
Esto casi funciona perfectamente, excepto que realmente lo necesito para eliminar la duplicación de los saltos de línea, ya que los párrafos tienden a estar separados por 2 o más, lo que resulta en </p><p></p><p>
.
¿Es posible lograr que solo lo reemplace una vez por párrafo?
Solución
disable-output-escaping
No es malo en sí mismo, pero hay sólo unos pocos casos en los que debes usarlo y este no es uno de ellos.En XSLT se trabaja con árboles, no con cadenas de marcado.Aquí hay una solución 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="' '"/>
<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>
Otros consejos
Pruebe esto (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> ( |\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>
Generalmente es una mala idea utilizar cadenas literales para incluir el marcado XML, ya que no se puede garantizar que los resultados estén equilibrados.
Dadas las funciones XPath a las que estás llamando y que no recuerdo haber tenido el lujo de tener en mi trabajo con MSXSL, parece que estás usando un procesador compatible con XPath 2.
Si ese es el caso, ¿Xpath 2 no tiene una función de reemplazo (cadena, patrón, reemplazo) que toma una expresión regular como segundo parámetro?
<xsl:value-of
select="replace(string(.), ' (\s| )*', '</p><p>')" />
Podría ser útil tener alguna entrada Xml de muestra y saber qué procesador planea usar.
Según su ejemplo original, parece que todos los párrafos duplicados tienen un prefijo de solo espacios en blanco.Entonces, algo como esta ligera modificación podría eliminar a los incautos.
<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>