在一个文件中组合/合并类似的XML结构

时间:2014-11-18 09:36:06

标签: xml structure xmlnode path-combine

我在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,尝试过,但我不明白,甚至不是一般的想法。还有其他想法或工具吗?

由于

1 个答案:

答案 0 :(得分:2)

对于它的价值,这是一种在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属性的元素可以合并。

  1. 所有正常节点(没有@name的节点)都按原样复制。第一个<xsl:template>就是这样做的(它是身份模板)。
  2. 我已经定义了一个&#34;可合并的群组&#34;元素作为沿其祖先共享一组共同@name属性值的元素。
    • 为此,我为拥有它们的所有元素创建所有相关@name属性的串联。
    • 目前,此转换可以处理深度为3级(concat(@name, '|', ancestor::*[1]/@name, '|', ancestor::*[2]/@name))的组。
    • 如有必要,以相同的方式添加更多关卡。
    • sowhat的父级的组名()为name2|name1||,这适用于该逻辑组中的其他<layer2>
  3. 现在每当XSLT引擎遇到带有@name的元素时,它就会出现
    • 计算该元素的$myKey)。
    • 获取具有相同键($myGroup)的元素组。
    • 查明当前元素是否是组中的第一个元素,如果是,则将其复制到输出
    • 有效地按键分组元素(这种技术称为 Muenchian分组)。
    • 然后它采取递归步骤:它开始处理该组的子项($myGroup/*)。
    • 实际上这会将我们带回0并且算法从头开始。
  4. 我的代码中有一些假设/限制可能不一定与您的输入一致。

    • 元素应该由他们的@name合并,而不是由其他一些属性合并。
    • 具有相同@name祖先的元素没有特殊属性,因此抛弃某个组中除第一个元素之外的每个元素都不会导致数据丢失。
    • 有一个有限的嵌套深度。
    • 可合并元素永远不是不可合并元素的后代(<layer> @name<layer>没有@name
    • 可能是其他人现在不知所措。

    阅读建议

    • 模板匹配和XSLT处理器的一般工作机制
    • XSL默认规则
    • 的XPath
    • XSL密钥和Muenchian分组
    • 身份模板
    • 整个处理流程中当前节点的概念