Я выполняю поиск и замену символа перевода строки ( ) и заменяю его тегами закрытия абзаца и открытия абзаца, используя следующий код:
<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>.
Можно ли сделать так, чтобы он заменял это только один раз за абзац?





Учитывая вызываемые вами функции 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>
Попробуйте это (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 - плохая идея, поскольку нельзя гарантировать сбалансированность результатов.
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>