Xml 在XSLT中递归地组合相同的同级元素
如何使用XSLT将具有相同名称和相同属性的所有同级元素合并到单个元素中?转换还应该递归地应用于正在合并的元素的子元素。这是源文档:Xml 在XSLT中递归地组合相同的同级元素,xml,xslt,Xml,Xslt,如何使用XSLT将具有相同名称和相同属性的所有同级元素合并到单个元素中?转换还应该递归地应用于正在合并的元素的子元素。这是源文档: <?xml version="1.0"?> <Root> <Element id="UniqueId1"> <SubElement1/> <SubElement2> <LeafElement1/> </SubElement2> </E
<?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>
具有相同名称和属性的任何元素都将组合到一个元素中。然后,检查他们的孩子。如果它们中的任何一个具有相同的名称和相同的属性,则它们将组合在一起。此转换递归地应用于所有元素
编辑:为了澄清,要合并两个元素,所有这些条件都必须为真
- 它们具有相同的元素名称
- 它们具有相同的属性
- 每个对应属性的值都相同
- 它们是兄弟元素(递归应用,因此在考虑其子元素之前,将合并和组合所有相同的父元素)
和
(相同的名称,相同的属性)
和
(相同的名称,相同的属性)
和
(不同名称)
和
(不同属性)
和
(不同的属性值)
<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属性上进行匹配,但在子元素上,您在标记名本身上进行匹配。那么子元素呢?谢谢你的评论,我添加了一个更明确的元素组合时间定义。我希望这能回答你的问题;我想这是有史以来最短的代码。。。