在格式化使用TEI标记(www.tei-c.org)的文本文档时会出现此问题。这超出了我的XSLT/XPATH技能。 (需要XSLT/XPATH 1.0中的解决方案。)

有一个标记元素<lb>,用于标记换行符。它可以采用@break属性。如果为@break="no",则在生成输出时,应忽略<lb>与周围文本之间的任何空格。

所以

This little tea <lb break="no" />
pot, short and stout.

应该理解为
This little teapot, short and stout.

也就是说,不应在输出流中呈现“tea”之后的空间和“pot”之前的换行符。

对于<lb>之前的空格,这可能会起作用:
<xsl:template match="text()[following-sibling::*[1][self::lb[@break='no']]">
    <!-- Do something about the space here. -->
</xsl:template>

类似的内容对于<lb>之后的换行符也适用。

好的。但这比较棘手:
This <emph>little <ref>tea </ref> </emph>
<lb break="no" />
pot, short and stout.

现在<ref>元素内的文本不是<lb>的同级对象。并且</ref>之前的空间,</emph>之前的空间以及<lb>之前和之后的换行符都需要从输出流中删除。

如何?

最佳答案

这是一个经过测试的有效实现,包括如何从文本节点的右侧或左侧修剪空白:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>

    <!-- Match if the preceding node (not necessarily sibling) that is either
      a non-empty-space-text node or an <lb> is an <lb break='no'> -->
    <xsl:template match="text()[
        (preceding::node()[
            self::text()[normalize-space() != ''] or
            self::lb])
                [last()]
        [self::lb[@break='no']]
        ]">

        <!-- Trim whitespace on the left. Thanks to Alejandro,
            http://stackoverflow.com/a/3997107/423105 -->
        <xsl:variable name="firstNonSpace"
            select="substring(normalize-space(), 1, 1)"/>
        <xsl:value-of select="concat($firstNonSpace,
            substring-after(., $firstNonSpace))"/>
    </xsl:template>

    <!-- Match if the next node (not necessarily sibling) that is either
      a non-empty-space-text node or an <lb> is an <lb break='no'> -->
    <xsl:template match="text()[
        following::node()[
            self::text()[normalize-space() != ''] or
            self::lb]
               [1]
        [self::lb[@break='no']]
        ]">

        <xsl:variable name="normalized" select="normalize-space()"/>
        <xsl:if test="$normalized != ''">
            <xsl:variable name="lastNonSpace"
                select="substring($normalized, string-length($normalized))"/>
            <xsl:variable name="trimmedSuffix">
                <xsl:call-template name="substring-after-last">
                    <xsl:with-param name="string" select="."/>
                    <xsl:with-param name="delimiter" select="$lastNonSpace"/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:value-of select="substring(., 1, string-length(.) -
               string-length($trimmedSuffix))"/>
        </xsl:if>
        <!-- otherwise output nothing. -->
    </xsl:template>


    <!-- Thanks to Jeni Tennison:
        http://www.stylusstudio.com/xsllist/200111/post00460.html -->
    <xsl:template name="substring-after-last">
        <xsl:param name="string" />
        <xsl:param name="delimiter" />
        <xsl:choose>
            <xsl:when test="contains($string, $delimiter)">
                <xsl:call-template name="substring-after-last">
                    <xsl:with-param name="string"
                        select="substring-after($string, $delimiter)" />
                    <xsl:with-param name="delimiter" select="$delimiter" />
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise><xsl:value-of select="$string" /></xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

在此之前,我的假设有待上述“下一个歧义”评论的答案,那就是,如果存在一个没有<lb>break="no"元素,则<lb>构成了“环绕文本”,因为它是忽略空白的边界。

输入样例:
<test>
    <t1>
        This <emph>little <ref>tea </ref> </emph>
        <lb break="no" />
        pot, short and stout.
    </t1>
    <t2>
        This <emph>little <ref>tea </ref> </emph>
        <lb />
        <lb break="no" />
        pot, short and stout.
    </t2>
</test>

输出:
<test>
    <t1>
        This <emph>little <ref>tea</ref></emph><lb break="no"/>pot, short and stout.
    </t1>
    <t2>
        This <emph>little <ref>tea </ref> </emph>
        <lb/><lb break="no"/>pot, short and stout.
    </t2>
</test>

此输出是正确的AFAICT。如果没有,请告诉我原因,我将解决问题。

关于XSLT当元素提示时,如何修剪元素前后的空间?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10127687/

10-11 22:23