可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want the output xml to have grouped for the element 'c', according to the attribute 'f'. Here is my input xml and the xslt. I want the group to occur only once and the other nodes should be copied as is to the output. The xslt i tried, copies the entire input xml. So if there are two or more elements with c element and same attribute value for 'f', want the first occurence of that group to the output. My wanted result is also copied.
input 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>
wanted output 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>
xslt i tried
<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>
回答1:
This transformation:
<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>
when applied on the provided XML document:
<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>
produces the wanted, correct result:
<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>
Explanation:
Proper use of the Muenchian grouping method.
回答2:
A simple solution would be to just add an empty template for all following c
nodes:
<xsl:template match="c[generate-id() = generate-id(key('mykey',@f)[position() > 1])]" />
回答3:
One idea might be to save all values of c in a variable in a format that allows you to differentiate them from one-another, and then every time you encounter c, you check to see if that value is contained within the variable. If it is, skip to the next node. If it isn't, continue processing the current node.
Tell me if you need more specific information
EDIT: As an alternative, and probably an easier method (I've been using NAnt recently, so I might be giving you a NAnt strategy) is to sort all the nodes by their s values. Then just have a variable that stores the current value of c and compare until the value you're looking at isn't equal to the stored value. Then reassign the value and do it again!
回答4:
You can match <a>
elements and check whether there are any preceding siblings with the same f
attribute in their <c>
sub-elements. If there are, you've found a duplicate of a given f
value (an occurrence of a given f
value that is not the first occurrence of that value) and you can just override the identity template to skip the element:
<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>
An advantage of this solution is that it doesn't require any knowledge about key or ID generation; it just works with basic XPath axis features. However, it might get slightly more complicated when the elements to compare are not all on the same nesting depth/in the same relative element hierarchy.
P.S.: I removed the <xsl:strip-space elements="*"/>
element because I couldn't test it (my Xml processor claimed I can only use it if I pass a readable stream rather than a file), but feel free to re-insert it if it works for you.