如何使用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>
具有相同名称和属性的任何元素都合并为一个元素。然后,他们的孩子接受检查。如果其中任何一个具有相同的名称和相同的属性,则将它们组合在一起。此转换以递归方式应用于所有元素。
编辑:为了澄清,所有这些条件必须为两个要合并的元素。
这些元素是相同的,应该合并:
<Item/>
和<Item/>
(同名,相同的属性)<Item Attr="foo"/>
和<Item Attr="foo"/>
(同名,相同的属性)这些元素不相同,不应合并:
<Item/>
和<SubItem/>
(姓名不同)<Item Attr="foo"/>
和<Item/>
(不同属性)<Item Attr="foo"/>
和<Item Attr="bar"/>
(不同的属性值)答案 0 :(得分:3)
我能想到的最简单的方法是在遇到具有该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>
这是我的头脑,所以可能需要一些调整。
为了让这个工作起来,recursivly是一个更大的问题。在第一个Element标记中处理SubElement2时,需要解析第二个Element中的SubElement2。这将使得处理任意深度变得相当复杂 我不知道你的特定用例,但简单的答案可能是重复运行上面的转换,直到结果与输入相同。
扩展if语句以激发具有相同名称的元素应该很容易。
答案 1 :(得分:3)
这应该做的工作:
<?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>
棘手的部分是 same-elements 的定义;按名称索引属性对于验证所有属性的真实性是必需的。
答案 2 :(得分:1)
如果您使用的是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的常见问题:http://www.dpawson.co.uk/xsl/sect2/N4486.html