【问题标题】:Is it possible to group child nodes based on element name in xslt 1.0?是否可以根据 xslt 1.0 中的元素名称对子节点进行分组?
【发布时间】:2014-06-07 09:47:58
【问题描述】:

我有以下输入(xml 的 sn-p):

<prompt>
    This is a sentence with <b>bold</b>.
    <p>
        This is in a paragraph element.
    </p>
    This is a <i>second</i> <b>sentence</b>.
</prompt>

我有一个匹配 pprompt 的模板。在该模板中,它将内容包装在 &lt;PARAGRAPH&gt; 元素中。

对于上面的例子,期望的输出是:

<PARAGRAPH>
    This is a sentence with bold.
</PARAGRAPH>
<PARAGRAPH>
    This is in a paragraph element.
</PARAGRAPH>
<PARAGRAPH>
    This is a second sentence.
</PARAGRAPH>

有没有办法根据元素名称将提示的内容分成几组?具体分组在非 p 和 p 的元素名称下?

我一直在阅读 XSLT 中的 Muenchian 分组策略,但不知道如何应用,甚至不知道是否可以这样做。也许我应该用另一种方式来解决这个问题?

【问题讨论】:

    标签: xml xslt xslt-1.0


    【解决方案1】:

    有没有办法将提示的内容分成几组 基于元素名称?

    这并不是您向我们展示的内容 - 您的组是基于 &lt;p&gt; 元素作为 text 节点之间的分隔符。

    我一直在阅读有关 XSLT 中的 Muenchian 分组策略的信息,但是 看不到如何申请,甚至无法申请。

    这是可能的 - 但在这种情况下,这根本不是微不足道的。按照以下思路尝试:

    XSLT 1.0

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:key name="group-by-divider" match="prompt/node()[not(self::p)]" use="count(preceding-sibling::p)" />
    
    <xsl:template match="/">
        <root>
            <xsl:apply-templates select="prompt"/>
        </root>
    </xsl:template>
    
    <xsl:template match="prompt">
        <xsl:apply-templates mode="group"/>
    </xsl:template>
    
    <xsl:template match="node()[not(self::p)][count(. | key('group-by-divider', count(preceding-sibling::p))[1]) = 1]" mode="group">
        <PARAGRAPH>
            <xsl:apply-templates select="key('group-by-divider', count(preceding-sibling::p))"/>
        </PARAGRAPH>
    </xsl:template>
    
    <xsl:template match="p" mode="group">
        <PARAGRAPHx>
            <xsl:apply-templates/>
        </PARAGRAPHx>
    </xsl:template>
    
    <!-- suppress nodes other than through the key -->
    <xsl:template match="node()" mode="group"/>
    
    </xsl:stylesheet>
    

    您可能想要添加一些空白管理以使其更漂亮。

    【讨论】:

    • 非常感谢!我必须修改密钥以说明存在的其他提示节点,我知道我没有在原始帖子中指定。以防万一其他人想知道,我计算了先前提示节点的数量并与前兄弟的计数连接起来。 concat(count(preceding-sibling::p), '-', count(preceding::prompt)) 也许有更好的方法来做到这一点?无论如何,感谢您的帮助!绝对让我度过了这个障碍。 :)
    【解决方案2】:
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="prompt">
    <yourRoot>
    <xsl:for-each-group select="node()" group-adjacent="name() = 'p'">
    <PARAGRAPH>
    <xsl:apply-templates select="current-group()"/>
    </PARAGRAPH>
    </xsl:for-each-group>
    </yourRoot>
    </xsl:template>
    

    【讨论】:

    • 这个问题被标记为 XSLT 1.0。标题中也是这么说的。
    • 谢谢,以后不会忽略XSLT版本了。
    【解决方案3】:

    以下样式表通过应用模板生成由&lt;p&gt; 元素分隔的字符串,然后使用递归模板将该字符串拆分为&lt;paragraph&gt; 元素。

    <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output indent="yes"/>
    
        <xsl:param name="delimiter" select="'|'"/>
    
        <xsl:template match="prompt">
            <xsl:call-template name="split-to-paragraph">
                <xsl:with-param name="txt">
                    <xsl:apply-templates select="node()" />
                </xsl:with-param>
            </xsl:call-template>
        </xsl:template>
    
        <xsl:template match="prompt/node()">
            <xsl:value-of select="."/>
        </xsl:template>
    
        <xsl:template match="prompt/p" priority="1">
            <xsl:value-of select="concat($delimiter, . ,$delimiter)"/>
        </xsl:template>
    
        <xsl:template name="split-to-paragraph">
            <xsl:param name="delimiter" select="$delimiter"/>
            <xsl:param name="txt"/>
            <xsl:choose>
                <xsl:when test="contains($txt, $delimiter)">
                    <PARAGRAPH>
                      <xsl:value-of select="normalize-space(
                                             substring-before($txt, $delimiter))"/>
                    </PARAGRAPH>
                    <xsl:call-template name="split-to-paragraph">
                       <xsl:with-param name="delimiter" select="$delimiter"/>
                       <xsl:with-param name="txt" 
                                       select="substring-after($txt, $delimiter)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <PARAGRAPH>
                        <xsl:value-of select="normalize-space($txt)"/>
                    </PARAGRAPH>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
    </xsl:stylesheet>
    

    【讨论】:

    • +1,很好。顺便说一句,您可以在递归调用中删除分隔符参数。
    • 我可能应该使用不同的变量名来区分全局 xsl:param 和模板 xsl:param,但故意断言当前的 $delimiter 值,以便递归模板将保留该值最初是在第一次调用模板时发送的(可能有不同的值,而不是依赖于默认行为)。
    猜你喜欢
    • 2021-03-14
    • 2011-07-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 2014-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多