使用 XSLT 枚举同名节点
Posted
技术标签:
【中文标题】使用 XSLT 枚举同名节点【英文标题】:Use XSLT to enumerate Nodes with the same name 【发布时间】:2011-03-06 07:22:33 【问题描述】:我有许多 XML 文件,这些文件通常包含多次节点(每次都包含不同的数据)。 示例:
<?xml version="1.0" encoding="UTF-8"?>
<SomeName>
<Node>
DataA
</Node>
<Node>
DataB
</Node>
<Node>
DataC
</Node>
<AnotherNode>
DataD
</AnotherNode>
<AnotherNode>
DataE
</AnotherNode>
<AnotherNode>
DataF
</AnotherNode>
<SingleNode>
DataG
</SingleNode>
</SomeName>
所需的输出是:
<?xml version="1.0" encoding="UTF-8"?>
<SomeName>
<Node1>
DataA
</Node1>
<Node2>
DataB
</Node2>
<Node3>
DataC
</Node3>
<AnotherNode1>
DataD
</AnotherNode1>
<AnotherNode2>
DataE
</AnotherNode2>
<AnotherNode3>
DataF
</AnotherNode3>
<SingleNode>
DataG
</SingleNode>
</SomeName>
问题是,我没有所有重复节点名的列表,因此我需要 XSLT 运行所有节点,并且只对存在多次的节点进行编号。那可能吗?
有没有人知道如何做到这一点?
谢谢!
【问题讨论】:
好问题 (+1)。请参阅我的答案以获得完整且更有效的解决方案。 【参考方案1】:您可以使用count(preceding-sibling::*[name(.) = name(current())])
获取与上下文元素同名的前面的兄弟元素的数量,并使用<xsl:element name="concat(name(.),'n')" />
创建一个与上下文元素同名的元素,并附加字母'n'给它。结合这些事实应该可以让你达到你想要的效果。
【讨论】:
使用count(preceding-sibling::*[something])
效率很低(O(N^2))。请参阅我的答案以获得更有效的解决方案。【参考方案2】:
这是一个完整的解决方案。 建议使用 Muenchian 方法进行分组,而不是基于count(preceding::*[someCondition])
进行分组,这种方法效率极低——O(N^2)。
这种转变:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kElsByName"
match="/*/*" use="name()"/>
<xsl:template match="/*">
<SomeName>
<xsl:for-each select=
"*[generate-id()
=
generate-id(key('kElsByName', name())[1])
]
">
<xsl:variable name="vsameNamedNodes" select=
"key('kElsByName', name())"/>
<xsl:variable name="vNumSameNamedNodes" select=
"count($vsameNamedNodes)"/>
<xsl:for-each select="$vsameNamedNodes">
<xsl:element name="concat(name(),
substring(position(),
1 div ($vNumSameNamedNodes > 1)
)
)
">
<xsl:copy-of select="node()"/>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
</SomeName>
</xsl:template>
</xsl:stylesheet>
应用于提供的 XML 文档时:
<SomeName>
<Node>
DataA
</Node>
<Node>
DataB
</Node>
<Node>
DataC
</Node>
<AnotherNode>
DataD
</AnotherNode>
<AnotherNode>
DataE
</AnotherNode>
<AnotherNode>
DataF
</AnotherNode>
<SingleNode>
DataG
</SingleNode>
</SomeName>
产生想要的结果:
<SomeName>
<Node1>
DataA
</Node1>
<Node2>
DataB
</Node2>
<Node3>
DataC
</Node3>
<AnotherNode1>
DataD
</AnotherNode1>
<AnotherNode2>
DataE
</AnotherNode2>
<AnotherNode3>
DataF
</AnotherNode3>
<SingleNode>
DataG
</SingleNode>
</SomeName>
【讨论】:
哇。这也很好用而且很快!非常感谢那个 XSLT!我所做的只是将 method="xml" 放入标头以具有 XML 输出,否则您的解决方案是完美的。再次感谢! 实际上,是否有可能将其更改为不重命名唯一节点? XSLT 基本上没有重命名 SingleNode?谢谢! @Grinner:是的,但是将新解决方案写到评论中很不方便。我可以编辑我的答案以包括最后一个要求。 @Grinner:我编辑了我的答案。该解决方案现在可以准确地产生您想要的输出。以上是关于使用 XSLT 枚举同名节点的主要内容,如果未能解决你的问题,请参考以下文章