正如@LarsH 已经指出的那样,由于没有向我们展示预期的输出 XML,我们只能猜测您真正想要什么。也就是说,这是我对 XSLT 1.0 解决方案的尝试。
当这个 XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key
name="PropertiesByAttributeNameVal"
match="Properties"
use="concat(name(@*[1]), '+', @*[1])" />
<xsl:key
name="ItemsByAttributeNameVal"
match="Items"
use="concat(name(@*[1]), '+', @*[1])" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Rootnode">
<Rootnode>
<xsl:apply-templates
select="Properties[
generate-id() =
generate-id(key(
'PropertiesByAttributeNameVal',
concat(name(@*[1]), '+', @*[1]))[1])]">
<xsl:with-param name="pKeyName"
select="'PropertiesByAttributeNameVal'" />
<xsl:sort select="concat(name(@*[1]), '+', @*[1])" />
</xsl:apply-templates>
<xsl:apply-templates
select="Items[
generate-id() =
generate-id(key(
'ItemsByAttributeNameVal',
concat(name(@*[1]), '+', @*[1]))[1])]">
<xsl:with-param name="pKeyName"
select="'ItemsByAttributeNameVal'" />
<xsl:sort select="concat(name(@*[1]), '+', @*[1])" />
</xsl:apply-templates>
</Rootnode>
</xsl:template>
<xsl:template match="Properties|Items">
<xsl:param name="pKeyName" />
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates
select="key($pKeyName, concat(name(@*[1]), '+', @*[1]))/*">
<xsl:sort select="name()" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
...针对提供的 XML 运行:
<Rootnode>
<Properties Attribute="xxx">
<Type>1</Type>
<Size>10</Size>
</Properties>
<Other>
<blah>h</blah>
</Other>
<Other2>
<blah>h</blah>
</Other2>
<Properties Attribute="xxx">
<xType>5</xType>
<xSize>10</xSize>
</Properties>
<Items>
<Item4>8</Item4>
</Items>
<Items>
<Item6>8</Item6>
</Items>
<Properties Attribute="xxx">
<zType>1</zType>
<zSize>10</zSize>
</Properties>
<Items place="UK">
<Item1>8</Item1>
</Items>
</Rootnode>
...我猜是生成了正确的输出 XML:
<?xml version="1.0"?>
<Rootnode>
<Properties Attribute="xxx">
<Size>10</Size>
<Type>1</Type>
<xSize>10</xSize>
<xType>5</xType>
<zSize>10</zSize>
<zType>1</zType>
</Properties>
<Items>
<Item4>8</Item4>
<Item6>8</Item6>
</Items>
<Items place="UK">
<Item1>8</Item1>
</Items>
</Rootnode>
请注意,如果针对稍微修改的 XML 文档(具有更多分组等)运行相同的 XSLT:
<?xml version="1.0" encoding="utf-8"?>
<Rootnode>
<Properties Attribute="xxx">
<Type>1</Type>
<Size>10</Size>
</Properties>
<Other>
<blah>h</blah>
</Other>
<Other2>
<blah>h</blah>
</Other2>
<Properties Attribute="yyy">
<xType>5</xType>
<xSize>10</xSize>
</Properties>
<Items>
<Item4>8</Item4>
</Items>
<Items place="US">
<Item9>8</Item9>
</Items>
<Items>
<Item1>8</Item1>
</Items>
<Properties Attribute2="xxx">
<zType>1</zType>
<zSize>10</zSize>
</Properties>
<Properties Attribute="xxx">
<elephantType>5</elephantType>
<elephantSize>15</elephantSize>
</Properties>
<Items place="UK">
<Item1>8</Item1>
</Items>
</Rootnode>
...再次,我认为正确的答案是:
<?xml version="1.0"?>
<Rootnode>
<Properties Attribute="xxx">
<Size>10</Size>
<Type>1</Type>
<elephantSize>15</elephantSize>
<elephantType>5</elephantType>
</Properties>
<Properties Attribute="yyy">
<xSize>10</xSize>
<xType>5</xType>
</Properties>
<Properties Attribute2="xxx">
<zSize>10</zSize>
<zType>1</zType>
</Properties>
<Items>
<Item1>8</Item1>
<Item4>8</Item4>
</Items>
<Items place="UK">
<Item1>8</Item1>
</Items>
<Items place="US">
<Item9>8</Item9>
</Items>
</Rootnode>
假设:
- 我假设每个
<Properties>
and<Items>
元素只有一个属性,它应该是分组确定器。
- 如果以上不正确,至少,我假设该元素的第一个属性应该是分组确定器。
解释:
因为这是一个 XSLT 1.0 解决方案,Muenchian Grouping
所以在唯一选择器下对节点和属性进行分组时的顺序;因此,我们定义了两个键:一个用于<Properties>
元素,一个用于<Items>
元素。
第一个模板是Identity Transform
- 它的工作是将源文档中的所有节点和属性按原样输出到结果文档。
第二个模板匹配<Rootnode>
元素。指示仅将模板应用于最先出现在其各自键中的那些<Properties>
和元素;<Items>
这具有仅处理唯一元素的预期效果(基于它们的第一个属性的名称和值)。
指定元素时<xsl:apply-templates>
,请注意,在这两种情况下,都指示按照相同的属性名称/值对对结果进行排序。
请注意,每个<xsl:apply-templates>
元素都有一个参数(通过<xsl:with-param>
)。正如您将看到的,处理<Properties>
和<Items>
元素的代码几乎相同。唯一的区别是我们从中获取结果的关键。因此,我选择将该逻辑整合到第三个模板中,并通过此参数考虑可变性。
第三个模板同时匹配<Properties>
和<Items>
元素。对于每一个,原始节点都被复制(就像它的属性一样)。最后,将模板应用于该元素的所有子元素(进行适当的排序[这一次,基于子元素本身的名称])。