xml - xslt 1.0 合并空元素名称

标签 xml xslt xslt-1.0

一个关于使用 xslt 1.0 的快速问题,您可能会帮助我。我有一个如下所示的输入 xml

<Root>
    <FirstName>Bob</FirstName>
    <LastName>Marley</LastName>
    <ID>BM1234</ID>
    <Songs>
        <Song>
            <EmptyElements></EmptyElements>
            <SongName>No woman no cry</SongName>
            <Year>1974</Year>
            <album></album>
            <studio></studio>
            <rating></rating>
        </Song>
    </Songs>
</Root>

输出需要看起来像

<Root>
    <FirstName>Bob</FirstName>
    <LastName>Marley</LastName>
    <ID>BM1234</ID>
    <Songs>
        <Song>
            <EmptyElements>album, studio, rating</EmptyElements>
            <SongName>No woman no cry</SongName>
            <Year>1974</Year>
        </Song>
    </Songs>
</Root>

所以基本上是将所有空元素的逗号分隔列表放入 EmptyElements 标记中。

最佳答案

或者简单地说:

XSLT 1.0

<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:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="Song">
    <xsl:copy>
        <EmptyElements>
            <xsl:for-each select="*[not(node() or self::EmptyElements)]">
                 <xsl:value-of select="name()"/>
                 <xsl:if test="position()!=last()">
                    <xsl:text>, </xsl:text>
                 </xsl:if>
            </xsl:for-each> 
        </EmptyElements>
        <xsl:apply-templates select="*[node()]"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

注意事项:

这个解决方案自豪地使用了 last() 函数。没有与使用此功能相关的性能问题。

XPath 规范指出:

The last function returns a number equal to the context size from the expression evaluation context.

XSLT 规范告诉我们:

Expression evaluation occurs with respect to a context. ... The context consists of:

• a node (the context node)
• a pair of non-zero positive integers (the context position and the context size)
...

处理器将返回并为列表中的每个节点一次又一次地计算当前节点列表中的所有节点的想法简直是荒谬的。一旦建立了上下文(通过调用 xsl:for-eachxsl:apply-templates),上下文大小就已知并且不会改变。

这个结论也可以很容易地进行测试:使用 10k 项的列表,在评估时没有发现明显的差异:

<xsl:for-each select="item">
    <xsl:value-of select="position()!=last()"/>
</xsl:for-each>

反对:

<xsl:for-each select="item">
    <xsl:value-of select="not(position() = 1)"/>
</xsl:for-each>

(使用 libxslt、Xalan 和 Saxon 测试)。

关于xml - xslt 1.0 合并空元素名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29908609/

相关文章:

c++ - 如何使用 boost 属性树提取 xml 文件的 DOCTYPE 节点?

c# - xml.LoadData - 根级别的数据无效。第 1 行,位置 1

java.lang.IllegalStateException : ScrollView can host only one direct child

xml - 通过 XSLT 使用 CDATA 部分包围 XML 元素

java - xml java xslt 标签

xslt - 按 xml 字母顺序对数据进行排序

java - XSL 为用户生成 UUID 并相应地替换其经理引用

XSLT:两个文件之间的 XPath 比较

xslt - 将 2 个数字相乘,然后求和

perl - XSLT 1 纯文本间距