I have an XML similar to this:
<Test>
<grapes>
<a>TypeA</a>
<b>value1</b>
</grapes>
<oranges>
开发者_开发百科<a>TypeB</a>
<b>value2</b>
</oranges>
<apples>
<a>TypeA</a>
<b>value3</b>
</apples>
</Test>
where the values are unique but the Type, might be the same.
I am trying to sort it so that the output is similar to this:
<group type="TypeA">
<value v="value1" />
<value v="value3" />
</group>
<group type="TypeB">
<value v="value2" />
</group>
I am having a hard time making sure the groups are unique in the output and the values are in the right group.
How should my XSL be structured?
Here is a much simpler solution (completely "push style", no <xsl:for-each>
, no nesting, no <xsl:variable>
, no current()
, , no //
, no axes):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kGoodsByType" match="/*/*" use="a"/>
<xsl:template match=
"/*/*[generate-id()
=
generate-id(key('kGoodsByType', a)[1])
]
">
<group type="{a}">
<xsl:apply-templates select="key('kGoodsByType', a)/b"/>
</group>
</xsl:template>
<xsl:template match="b">
<value v="{.}"/>
</xsl:template>
<xsl:template match="*/* | text()" priority="-1"/>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<Test>
<grapes>
<a>TypeA</a>
<b>value1</b>
</grapes>
<oranges>
<a>TypeB</a>
<b>value2</b>
</oranges>
<apples>
<a>TypeA</a>
<b>value3</b>
</apples>
</Test>
the wanted, correct result is produced:
<group type="TypeA">
<value v="value1"/>
<value v="value3"/>
</group>
<group type="TypeB">
<value v="value2"/>
</group>
Explanation: Muenchian grouping of /*/*
using as key the string values of their a
children.
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:for-each-group select="*/a" group-by=".">
<group type="{current-grouping-key()}">
<xsl:sequence select="current-group()/../b"/>
</group>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
When this transformation is performed on the same XML document (above), the same correct result is produced:
<group type="TypeA">
<b>value1</b>
<b>value3</b>
</group>
<group type="TypeB">
<b>value2</b>
</group>
Explanation:
<xsl:for-each-group>
current-group()
current-grouping-key()
XSLT 1.0 :
You start by creating unique groups for your types using the muenchian method. Google it to find out what it is. Then it's just a matter of iterating through them and printint out what you want, how you want it :
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="types" match="a" use="text()"/>
<xsl:template match="/">
<result>
<xsl:for-each select="//a[generate-id(.) = generate-id(key('types', text())[1])]">
<group type="{current()/text()}">
<xsl:for-each select="//a[text() = current()/text()]">
<xsl:variable name="values" select="following-sibling::b | preceding-sibling::b"/>
<xsl:for-each select="$values">
<value v="{current()}"/>
</xsl:for-each>
</xsl:for-each>
</group>
</xsl:for-each>
</result>
</xsl:template>
</xsl:stylesheet>
You will find that the output is identical to what you expect.
My proposal is slightly different from FailedDev's:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/Test">
<root>
<xsl:for-each select="*[a != following::a]">
<xsl:sort select="a" data-type="text" />
<group type="{a}">
<xsl:for-each select="/Test/*[a = current()/a]">
<xsl:sort select="b" data-type="text" />
<value v="{b}" />
</xsl:for-each>
</group>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
The outer
<xsl:for-each select="*[a != following::a]" />
select all unique types, ie. TypeA and TypeB. The
<xsl:sort select="a" data-type="text" />
sorts these according to name, making sure TypeA will appear above TypeB. The inner
<xsl:for-each select="/Test/*[a = current()/a]" />
selects a list of unique values for each type, ie. for TypeA the values value1 and value3 are extracted. Again, the resulting list is sorted to list value1 before value3.
精彩评论