我希望输出xml根据属性"f"为元素"c"分组。这是我的输入 xml 和 xslt。我希望该组只出现一次,其他节点应按原样复制到输出中。我尝试的 xslt 复制了整个输入 xml。因此,如果有两个或多个元素具有 c 元素和相同的 'f' 属性值,则希望该组的第一次出现到输出中。我想要的结果也被复制了。
输入 XML
<M>
<a>
<b>
<c f="123">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<c f="123">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<c f="567">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<somethingelse></somethingelse>
</b>
</a>
</M>
想要的输出 XML
<M>
<a>
<b>
<c f="123">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<c f="567">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<somethingelse></somethingelse>
</b>
</a>
</M>
我试过了
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="mykey" match="c"
use="@f"/>
<xsl:template match=
"c[generate-id()
=
generate-id(key('mykey',@f)[1])
]
">
<xsl:text/>
<xsl:copy-of select="key('mykey',@f)[1]"/>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
此转换:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kAByC-F" match="a" use="*/c/@f"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"a[*/c
and
not(generate-id()
=
generate-id(key('kAByC-F', */c/@f)[1])
)
]"/>
</xsl:stylesheet>
应用于提供的 XML 文档时:
<M>
<a>
<b>
<c f="123">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<c f="123">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<c f="567">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<somethingelse></somethingelse>
</b>
</a>
</M>
产生所需的正确结果:
<M>
<a>
<b>
<c f="123">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<c f="567">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<somethingelse/>
</b>
</a>
</M>
解释:
正确使用明基分组方法。
解决方案是为以下所有节点添加一个空模板c
:
<xsl:template match="c[generate-id() = generate-id(key('mykey',@f)[position() > 1])]" />
一个想法可能是将 c 的所有值保存在变量中,以允许您将它们彼此区分的格式,然后每次遇到 c 时,您都会检查该值是否包含在变量中。如果是,请跳到下一个节点。如果不是,请继续处理当前节点。
告诉我您是否需要更具体的信息
编辑:作为替代方案,可能是一种更简单的方法(我最近一直在使用 NAnt,所以我可能会给你一个 NAnt 策略)是按其值对所有节点进行排序。然后只需有一个存储 c 当前值的变量并进行比较,直到您正在查看的值不等于存储的值。然后重新分配值并再次执行!
您可以匹配<a>
元素,并检查其<c>
子元素中是否有任何具有相同f
属性的先前同级。如果有,则您已经找到了给定f
值的副本(给定f
值的出现,但不是该值的第一次出现),您只需覆盖标识模板即可跳过该元素:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/M/a[b/c/@f = preceding-sibling::a/b/c/@f]"/>
</xsl:stylesheet>
此解决方案的一个优点是它不需要任何有关密钥或 ID 生成的知识;它只适用于基本的 XPath 轴功能。但是,当要比较的元素并非都位于相同的嵌套深度/相同的相对元素层次结构中时,情况可能会稍微复杂一些。
PS:我删除了<xsl:strip-space elements="*"/>
元素,因为我无法测试它(我的 Xml 处理器声称我只能在传递可读流而不是文件时才使用它),但如果它适合您,请随时重新插入它。