Вопрос
Я выполняю поиск и замену символа перевода строки (
) и заменив его тегами закрытия абзаца и открытия абзаца, используя следующий код:
<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>
Это работает почти идеально, за исключением того, что мне действительно нужно, чтобы отключить перевод строк, поскольку абзацы, как правило, разделяются на 2 или более, что приводит к </p><p></p><p>
.
Можно ли сделать так, чтобы он заменял это только один раз в каждом абзаце?
Решение
disable-output-escaping
само по себе это не зло, но есть лишь несколько случаев, когда вам следует его использовать, и это не один из них.В XSLT вы работаете с деревьями, а не со строкой разметки.Вот решение 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>
Другие советы
Попробуйте это (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>
Как правило, использовать литеральные строки для размещения в XML-разметке - плохая идея, поскольку вы не можете гарантировать сбалансированность результатов.
Учитывая вызываемые вами функции XPath, которые я не помню, чтобы у меня была такая роскошь в моей работе с MSXSL, похоже, что вы используете процессор, совместимый с XPath 2.
Если это так, разве в XPath 2 нет функции replace (строка, шаблон, замена), которая принимает регулярное выражение в качестве второго параметра?
<xsl:value-of
select="replace(string(.), ' (\s| )*', '</p><p>')" />
Возможно, было бы полезно иметь некоторые образцы входных данных Xml и знать, какой процессор вы планируете использовать.
Из вашего исходного примера кажется, что все повторяющиеся абзацы имеют префикс только для пробелов.Так что что-то вроде этой небольшой модификации может урезать количество обманутых.
<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>