XSL normalize-space() 对嵌入式标签过于贪婪

XSL normalize-space() is too greedy around embedded tags

我认为这很简单。这是我的意见。我无法控制它的布局。

<?xml version="1.0" encoding="UTF-8"?>
<topic>
    <title>The Torments of Hell</title>
    <body>
        <p>Life is a <xref href="dungeon.xml">dungeon
            </xref> and
            an <xref href="abyss.xml">abyss</xref>.
        </p>
    </body>
</topic>

我试图获得的输出:

...
Life is a<ref>[[dungeon|dungeon.xml]]</ref> and an <ref>[[abyss|abyss.xml]]</ref>.
...

所以所见即所得(另一个工具的输出,我无法控制,它将 ref 标签转换为带引用的脚注)看起来像这样:

生活是地牢1也是深渊2.

这是我开始使用的 xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:template match="topic">
        <xsl:text>&#xa;=</xsl:text>
        <xsl:value-of select="title"/>
        <xsl:text>=</xsl:text>
        <xsl:apply-templates select="body/p"/>
    </xsl:template>
    <xsl:template match="p">
        <xsl:text>&#xa;&#xa;</xsl:text>
        <xsl:apply-templates select="node()"/>
    </xsl:template>
    <xsl:template match="xref">
        <xsl:text disable-output-escaping="yes">&lt;ref&gt;</xsl:text>
        <xsl:text>[[</xsl:text>
        <xsl:value-of select="."/>
        <xsl:text>|</xsl:text>
        <xsl:value-of select="@href"/>
        <xsl:text>]]</xsl:text>
        <xsl:text disable-output-escaping="yes">&lt;/ref&gt;</xsl:text>
    </xsl:template>
</xsl:stylesheet>

这是我得到的输出:

...
Life is a <ref>[[dungeon|dungeon.xml]]</ref> and
            an <ref>[[abyss|abyss.xml]]</ref>.
...

没问题,我将使用 normalize-space 去掉换行符:

<xsl:template match="text()">
    <xsl:value-of select="normalize-space(.)"/>
</xsl:template>

现在我的输出如下所示:

...
Life is a<ref>[[dungeon|dungeon.xml]]</ref>and an<ref>[[abyss|abyss.xml]]</ref>.
...

我的所见即所得看起来像这样:

生活是地下城1和深渊2

换行消失了,但是 ref 标签前后的 space 也消失了;这些我想保留。我可以破解它并在我的 ref 标签前后添加一个 space,但后来我得到了这个丑陋的东西:

生活是地牢1和深渊2 .

注意 abyss 和句点之间的 space。我尝试了解决方案 here and here,但这些解决方案只消除了额外的 space;他们对换行没有帮助。

我花了一整天时间尝试使用 XSL 来完成此操作,但没有成功。然后我花了 45 分钟写了一个 javascript ,它完全符合我的要求。实际的、直接的问题解决了,但我觉得奇怪的是,使用 XSL 会如此困难。看起来很简单。有没有一种方法可以使用 XSL 执行此操作,或者我是否需要在应用样式表之前预处理 XML?

您可以在没有 normalize-space 功能的情况下使用模板匹配来摆脱换行符,这里有一个例子:

<xsl:template match="yourText">
    <xsl:call-template name="replace">
        <xsl:with-param name="string" select="."/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="replace">
    <xsl:param name="string"/>
    <xsl:choose>
        <xsl:when test="contains($string, '&#10;')">
            <xsl:value-of select="substring-before($string, '&#10;')"/>
            <xsl:call-template name="replace">
                <xsl:with-param name="string" select="substring-after($string, '&#10;')"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$string"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

这将删除您文本中出现的所有 newline

Edit : 然后你可以在结果上调用 normalize-space 函数来去掉多余的空格。

鉴于您使用的是 XSLT 2.0 版,您可以使用带有 replace 函数的正则表达式来获取 normalize-space() 行为的 "squash runs of whitespace down to a single space" 部分,而无需同时获取 "and trim leading and trailing whitespace"部分。

<xsl:template match="text()">
    <xsl:value-of select="replace(., '\s+', ' ')"/>
</xsl:template>

这会将白色 space 的前导 and/or 尾随 运行 压缩为单个 space(与任何 运行 内部白色[一样=27=]) 但不会完全删除它们。

顺便说一句,您不需要对 ref 标签使用 disable-output-escaping,因为它们在模板中得到了适当的平衡。刚刚

<xsl:template match="xref">
    <ref>
        <xsl:text>[[</xsl:text>
        <xsl:value-of select="."/><!-- or <apply-templates/> -->
        <xsl:text>|</xsl:text>
        <xsl:value-of select="@href"/>
        <xsl:text>]]</xsl:text>
    </ref>
</xsl:template>

会很好。