我在XML文件中有以下结构:
<root name="name1">
<layer1 name="name2">
<layer2 attribute="sowhat">
</layer2>
</layer1>
</root>
<root name="name1">
<layer1 name="name2">
<layer2 attribute="justit">
</layer2>
</layer1>
</root>
<root name="name1">
<layer1 name="name2">
<layer2 attribute="yeaha">
</layer2>
</layer1>
</root>
<root name="name2123">
<layer1 name="name2">
<layer2 attribute="itis">
</layer2>
</layer1>
</root>
我想得到一个看起来像这样的结果:
<root name="name1">
<layer1 name="name2">
<layer2 attribute="sowhat"></layer2>
<layer2 attribute="justit"></layer2>
<layer2 attribute="yeaha"></layer2>
</layer1>
</root>
<root name="name2123">
<layer1 name="name2">
<layer2 attribute="itis">
</layer2>
</layer1>
</root>
所以我想尽可能地合并和组合节点。我还没有使用XSLT,尝试过,但我不明白,甚至不明白一般的想法。还有其他想法或工具吗?
谢谢
对于它的价值,这里有一种方法可以在 XSLT 1.0 中执行此操作。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="name" match="*[@name]" use="
concat(@name, '|', ancestor::*[1]/@name, '|', ancestor::*[2]/@name)
" />
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[@name]">
<xsl:variable name="myKey" select="
concat(@name, '|', ancestor::*[1]/@name, '|', ancestor::*[2]/@name)
" />
<xsl:variable name="myGroup" select="key('name', $myKey)" />
<xsl:if test="generate-id() = generate-id($myGroup[1])">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:apply-templates select="$myGroup/*" />
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
输出
<roots>
<root name="name1">
<layer1 name="name2">
<layer2 attribute="sowhat"/>
<layer2 attribute="justit"/>
<layer2 attribute="yeaha"/>
</layer1>
</root>
<root name="name2123">
<layer1 name="name2">
<layer2 attribute="itis"/>
</layer1>
</root>
</roots>
XSLT 的主要功能是能够在相对较少的代码行中表达复杂的转换。上面的转换是 29 行代码,你可以进一步压缩它。
我认为 XSLT 速成课程超出了这个答案的范围。除此之外,互联网上还有无数的 XSLT 速成课程。
所以我要做的是 我将对这里发生的事情进行总体概述。
首先,我为您的输入定义了两类元素 - 可合并的元素和不可合并的元素。我已经将所有具有@name
属性的元素定义为可合并的。
- 所有普通节点(没有
@name
的节点)都会按原样复制。第一个<xsl:template>
执行此操作(它是标识模板)。 - 我将一个"可合并的元素组"定义为沿其祖先共享一组公共
@name
属性值的元素。- 为此,我为所有具有这些属性的元素创建了所有相关
@name
属性的串联。 - 目前,这种转换可以处理 3 级深度 (
concat(@name, '|', ancestor::*[1]/@name, '|', ancestor::*[2]/@name)
) 的组。 - 如有必要,以相同的方式添加更多级别。
sowhat
父级的组名(键)是name2|name1||
,这适用于该逻辑组中的其他<layer2>
。
- 为此,我为所有具有这些属性的元素创建了所有相关
- 现在,每当XSLT引擎遇到具有
@name
的元素时,它- 计算该元素的键 (
$myKey
)。 - 获取具有相同键 (
$myGroup
) 的元素组。 - 找出当前元素是否是组中的第一个元素,如果是,则将其复制到输出
- 实际上,这按元素的键对元素进行分组(这种技术称为Muenchian分组)。
- 然后它采取递归步骤:它开始处理该组的子组(
$myGroup/*
)。 - 实际上,这将我们带回方格 0,算法从头开始。
- 计算该元素的键 (
我的代码中有一些假设/限制,可能不一定与您的输入一致。
- 这些要素应按其
@name
合并,而不是按其他属性合并。 - 具有相同
@name
祖先的元素没有特殊属性,因此丢弃除某个组中的第一个元素之外的每个元素不会导致数据丢失。 - 嵌套深度有限。 可合并元素
- 从来都不是不可合并元素的后代(没有
<layer>
在<layer>
内有@name
没有@name
) - 可能是其他人现在让我忘记了。
阅读推荐
- 模板匹配和 XSLT 处理器的一般工作机制
- XSL 默认规则
- XPath
- XSL 键和明基安分组
- 标识模板
- 整个处理流程中当前节点的概念