0

我在 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>

And I want to get a result that looks like:

<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>

So I want to merge and combine nodes as far as possible. I havent uses XSLT yet, tried it, but I dont get it, not even the general idea. Any other ideas or tools?

Thanks

4

1 回答 1

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. 所有正常节点(没有 a 的节点@name)都按原样复制。第一个<xsl:template>这样做(它是身份模板)。
  2. 我将元素的“可合并组”定义为@name沿其祖先共享一组公共属性值的元素。
    • 为此,我@name为拥有它们的所有元素创建了所有相关属性的串联。
    • 目前,这种转换可以处理深度为 3 级的组 ( concat(@name, '|', ancestor::*[1]/@name, '|', ancestor::*[2]/@name))。
    • 如有必要,以相同的方式添加更多级别。
    • 父级的组名(sowhat) is name2|name1||,这适用<layer2>于该逻辑组中的另一个。
  3. 现在,每当 XSLT 引擎遇到带有 a 的元素时@name,它
    • 计算该元素的( $myKey)。
    • 获取具有相同键 ( $myGroup) 的元素组。
    • 找出当前元素是否是组中的第一个元素,如果是,则将其复制到输出
    • 这有效地通过它们的键对元素进行分组(这种技术称为Muenchian 分组)。
    • 然后它采取递归步骤:它开始处理该组的孩子($myGroup/*)。
    • 实际上,这使我们回到了 0 方,算法从头开始。

我的代码中有一些假设/限制可能不一定与您的输入一致。

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

阅读建议

  • 模板匹配和 XSLT 处理器的一般工作机制
  • XSL 默认规则
  • XPath
  • XSL 键和 Muenchian 分组
  • 身份模板
  • 当前节点的概念贯穿整个处理流程
于 2014-11-18T10:38:44.090 回答