Xml 在XSLT中递归地组合相同的同级元素

Xml 在XSLT中递归地组合相同的同级元素,xml,xslt,Xml,Xslt,如何使用XSLT将具有相同名称和相同属性的所有同级元素合并到单个元素中?转换还应该递归地应用于正在合并的元素的子元素。这是源文档: <?xml version="1.0"?> <Root> <Element id="UniqueId1"> <SubElement1/> <SubElement2> <LeafElement1/> </SubElement2> </E

如何使用XSLT将具有相同名称和相同属性的所有同级元素合并到单个元素中?转换还应该递归地应用于正在合并的元素的子元素。这是源文档:

<?xml version="1.0"?>
<Root>
  <Element id="UniqueId1">
    <SubElement1/>
    <SubElement2>
      <LeafElement1/>
    </SubElement2>
  </Element>
  <Element id="UniqueId1">
    <SubElement2>
      <LeafElement1/>
      <LeafElement2/>
    </SubElement2>
    <SubElement3/>
  </Element>
  <Element id="UniqueId2">
    <SubElement1/>
    <SubElement4/>
  </Element>    
</Root>

应将其转变为:

<?xml version="1.0"?>
<Root>
  <Element id="UniqueId1">
    <SubElement1/>
    <SubElement2>
      <LeafElement1/>
      <LeafElement2/>
    </SubElement2>
    <SubElement3/>
  </Element>
  <Element id="UniqueId2">
    <SubElement1/>
    <SubElement4/>
  </Element>    
</Root>

具有相同名称和属性的任何元素都将组合到一个元素中。然后,检查他们的孩子。如果它们中的任何一个具有相同的名称和相同的属性,则它们将组合在一起。此转换递归地应用于所有元素

编辑:为了澄清,要合并两个元素,所有这些条件都必须为真

  • 它们具有相同的元素名称
  • 它们具有相同的属性
  • 每个对应属性的值都相同
  • 它们是兄弟元素(递归应用,因此在考虑其子元素之前,将合并和组合所有相同的父元素)
这些元素相同,应合并:

  • (相同的名称,相同的属性)
  • (相同的名称,相同的属性)
这些元素不相同,不应合并:

  • (不同名称)
  • (不同属性)
  • (不同的属性值)

我能想到的最简单的方法是在遇到具有相同ID的第一个元素时解析具有相同ID的所有元素。看起来是这样的:

<xsl:variable name="curID" select="@id"/>
<xsl:if test="count(preceding-sibling::*[@id=$curID])=0">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:for-each select="following-sibling::*[@id=$curID]">
      <xsl:apply-templates select="@*"/>
    </xsl:for-each>
    <xsl:apply-templates select="node()"/>
    <xsl:for-each select="following-sibling::*[@id=$curID]">
      <xsl:apply-templates select="node()"/>
    </xsl:for-each>
  </xsl:copy>
</xsl:if>

这是我的头顶,所以它可能需要一些调整

要让它递归地工作是一个更大的问题。在处理第一个元素标记中的SubElement2时,需要解析第二个元素中的SubElement2。这将变得相当复杂,无法处理任意深度。
我不知道您的具体用例,但最简单的答案可能是反复运行上述转换,直到结果与输入相同


扩展if语句以激发具有相同名称的元素应该很容易。

如果您使用的是XSLT2,则应该能够使用分组功能。以下是早期教程:

http://www.xml.com/pub/a/2003/11/05/tr.html
下面是由一个制作优秀教程的小组编写的后续教程:

http://www.zvon.org/xxl/XSL-Ref/Tutorials/index.html
如果您仅限于XSLT1,这是可能的,但更难


如果您仍有困难,请尝试Dave Pawson的常见问题解答:

这应该可以解决问题:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" indent="yes"/>
  <xsl:key name="atts-by-name" match="@*" use="name()"/>
  <xsl:template match="Root">
    <xsl:copy>
      <xsl:call-template name="merge">
        <xsl:with-param name="elements" select="*"/>
      </xsl:call-template>
    </xsl:copy>
  </xsl:template>
  <xsl:template name="merge">
    <xsl:param name="elements"/>
    <xsl:for-each select="$elements">
      <xsl:variable name="same-elements" select="$elements[name()=name(current()) and count(@*)=count(current()/@*) and count(@*[. = key('atts-by-name',name())[generate-id(..)=generate-id(current())]])=count(@*)]"/>
      <xsl:if test="generate-id($same-elements[1]) = generate-id()">
        <xsl:copy>
          <xsl:copy-of select="@*"/>
          <xsl:call-template name="merge">
            <xsl:with-param name="elements" select="$same-elements/*"/>
          </xsl:call-template>
        </xsl:copy>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>


棘手的部分是相同元素的定义;为验证所有属性的平等性,必须按名称索引属性

这个问题看起来有点不明确。在“元素”情况下,您在id属性上进行匹配,但在子元素上,您在标记名本身上进行匹配。那么子元素呢?谢谢你的评论,我添加了一个更明确的元素组合时间定义。我希望这能回答你的问题;我想这是有史以来最短的代码。。。