问题
I've revised the data file and added a Grouping column. I could not figure out a logic for grouping otherwise.
The data contains information for a stamp collection.
Here is sample XML:
<?xml version="1.0" encoding="UTF-8"?>
<stamps>
<stamp>
<Group>25</Group>
<Scott>3133</Scott>
<Title>32¢ Thornton Wilder</Title>
<Series>Literary Arts</Series>
</stamp>
<stamp>
<Group>26</Group>
<Scott>3134</Scott>
<Title>32¢ Charlie Chaplin</Title>
</stamp>
<stamp>
<Group>26</Group>
<Scott>3135</Scott>
<Title>32¢ Raoul Wallenberg</Title>
</stamp>
<stamp>
<Group>27</Group>
<Scott>3136</Scott>
<Title>Sheet of 15</Title>
<Issue>The World of Dinosaurs</Issue>
</stamp>
<stamp>
<Group>27</Group>
<Scott>3136</Scott>
<Minor>a</Minor>
<Title>32¢ Ceratosaurus</Title>
<Issue>The World of Dinosaurs</Issue>
</stamp>
<stamp>
<Group>27</Group>
<Scott>3136</Scott>
<Minor>b</Minor>
<Title>32¢ Camptosaurus</Title>
<Issue>The World of Dinosaurs</Issue>
</stamp>
<stamp>
<Group>27</Group>
<Scott>3136</Scott>
<Minor>c</Minor>
<Title>32¢ Camarasaurus</Title>
<Issue>The World of Dinosaurs</Issue>
</stamp></stamps>
Here is the XSLT I put together:
<?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" indent="yes"/>
<xsl:key name="StampGroup" match="stamp" use="Group"/>
<xsl:key name="ScottGroup" match="stamp" use="concat(Group, '|', Scott)"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="stamp[generate-id() = generate-id(key('StampGroup', Group)[1])]" mode="StampGroup" />
</xsl:copy>
</xsl:template>
<xsl:template match="stamp" mode="StampGroup">
<StampGroup id="{Group}">
<xsl:apply-templates select="key('StampGroup', Group)[generate-id() = generate-id(key('ScottGroup', concat(Group, '|', Scott))[1])]" mode="ScottGroup" />
</StampGroup>
</xsl:template>
<xsl:template match="stamp" mode="ScottGroup">
<stamp>
<Scott><xsl:value-of select="Scott"/></Scott>
<Title><xsl:value-of select="Title"/></Title>
<Minor><xsl:value-of select="Minor"/></Minor>
</stamp>
</xsl:template>
</xsl:stylesheet>
Here is the resulting XML:
<?xml version="1.0" encoding="utf-8"?>
<stamps>
<StampGroup id="25">
<stamp>
<Scott>3133</Scott>
<Title>32¢ Thornton Wilder</Title>
<Minor/>
</stamp>
</StampGroup>
<StampGroup id="26">
<stamp>
<Scott>3134</Scott>
<Title>32¢ Charlie Chaplin</Title>
<Minor/>
</stamp>
<stamp>
<Scott>3135</Scott>
<Title>32¢ Raoul Wallenberg</Title>
<Minor/>
</stamp>
<stamp>
<Scott>3136</Scott>
<Title>Sheet of 15</Title>
<Minor/>
</stamp>
</StampGroup>
</stamps>
It's mostly working. It's pulling the groups. It's pulling UNIQUE <Scott>
items, but it's not picking up the <Minor>
subitems.
I've never used this type of XSLT structure before. How do you get it to repeat items as in for-each?
Group 27 has 4 items in it. They all have the same <Scott>
number but different <Minor>
fields.
Do I need to create a third key?
Sorry, forgot to add the desired result:
<?xml version="1.0" encoding="utf-8"?>
<stamps>
<StampGroup id="25">
<stamp>
<Scott>3133</Scott>
<Title>32¢ Thornton Wilder</Title>
<Minor/>
</stamp>
</StampGroup>
<StampGroup id="26">
<stamp>
<Scott>3134</Scott>
<Title>32¢ Charlie Chaplin</Title>
<Minor/>
</stamp>
<stamp>
<Scott>3135</Scott>
<Title>32¢ Raoul Wallenberg</Title>
<Minor/>
</stamp>
</StampGroup>
<StampGroup id="27">
<stamp>
<Scott>3136</Scott>
<Title>Sheet of 15</Title>
<Minor/>
</stamp>
<stamp>
<Scott>3136</Scott>
<Title>32¢ Ceratosaurus</Title>
<Minor>a</Minor>
</stamp>
<stamp>
<Scott>3136</Scott>
<Title>32¢ Camptosaurus</Title>
<Minor>b</Minor>
</stamp>
<stamp>
<Scott>3136</Scott>
<Title>32¢ Camarasaurus</Title>
<Minor>c</Minor>
</stamp>
</StampGroup>
</stamps>
回答1:
If you want to have only unique Scott
values within each Group
, and only unique Minor
values within each Scott
subgroup, then yes, you will need three keys.
And if the values by which you want to subgroup are not unique to each parent group, then the keys will have to be concatenated in order to narrow the key down to matching items from the current parent group only.
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="stamp-by-group" match="stamp" use="Group" />
<xsl:key name="stamp-by-scott" match="stamp" use="concat(Group, '|', Scott)" />
<xsl:key name="stamp-by-minor" match="stamp" use="concat(Group, '|', Scott, '|', Minor)" />
<xsl:template match="/stamps">
<xsl:copy>
<xsl:for-each select="stamp[count(. | key('stamp-by-group', Group)[1]) = 1]">
<StampGroup id="{Group}">
<xsl:for-each select="key('stamp-by-group', Group)[count(. | key('stamp-by-scott', concat(Group, '|', Scott))[1]) = 1]">
<xsl:apply-templates select="key('stamp-by-scott', concat(Group, '|', Scott))[count(. | key('stamp-by-minor', concat(Group, '|', Scott, '|', Minor))[1]) = 1]"/>
</xsl:for-each>
</StampGroup>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="stamp">
<xsl:copy>
<xsl:copy-of select="Scott | Title"/>
<Minor><xsl:value-of select="Minor" /></Minor>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Edit:
If you're not creating anything for the group by Scott
, then you can skip the secondary grouping and its associated key, and go directly to the tertiary grouping:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="stamp-by-group" match="stamp" use="Group" />
<xsl:key name="stamp-by-minor" match="stamp" use="concat(Group, '|', Scott, '|', Minor)" />
<xsl:template match="/stamps">
<xsl:copy>
<xsl:for-each select="stamp[count(. | key('stamp-by-group', Group)[1]) = 1]">
<StampGroup id="{Group}">
<xsl:apply-templates select="key('stamp-by-group', Group)[count(. | key('stamp-by-minor', concat(Group, '|', Scott, '|', Minor))[1]) = 1]"/>
</StampGroup>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="stamp">
<xsl:copy>
<xsl:copy-of select="Scott | Title"/>
<Minor><xsl:value-of select="Minor" /></Minor>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
回答2:
Your problem is interesting. Basic order is ItemNo with grouping header on change, but in case of groups in Series or Issue there shall be another grouping header without changing the ItemNo order. I figured out the following and used a html table with background color for the grouping headers for easier overview. I didn't completely understand the expected output, I'm not a stamp expert. The script should give you the idea how to solve that.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="ItemNo" match="stamp" use="ItemNo" />
<xsl:key name="Series" match="stamp" use="Series" />
<xsl:key name="Issue" match="stamp" use="Issue" />
<xsl:template match="/">
<table border="1">
<xsl:apply-templates select="stamps/stamp[generate-id(.)=generate-id(key('ItemNo',ItemNo)[1])]" mode="groupItem">
<xsl:sort select="ItemNo"/>
</xsl:apply-templates>
</table>
</xsl:template>
<xsl:template match="stamp" mode="groupItem">
<xsl:variable name="varItemNo" select="ItemNo"/>
<xsl:apply-templates select="../stamp[ItemNo=$varItemNo][generate-id(.)=generate-id(key('Series',Series)[1])]" mode="groupSeries">
<xsl:sort select="ItemNo"/>
</xsl:apply-templates>
<xsl:apply-templates select="../stamp[ItemNo=$varItemNo][generate-id(.)=generate-id(key('Issue',Issue)[1])]" mode="groupIssue">
<xsl:sort select="Issue"/>
</xsl:apply-templates>
<tr style="background-color:yellow">
<td><xsl:value-of select="ItemNo"/></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<xsl:apply-templates select="../stamp[ItemNo=$varItemNo]">
<xsl:sort select="Series"/>
<xsl:sort select="Issue"/>
<xsl:sort select="Minor"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="stamp" mode="groupSeries">
<tr style="background-color:gold">
<td></td>
<td></td>
<td><xsl:value-of select="Series"/></td>
<td></td>
<td></td>
<td></td>
</tr>
</xsl:template>
<xsl:template match="stamp" mode="groupIssue">
<tr style="background-color:tan">
<td></td>
<td></td>
<td></td>
<td></td>
<td><xsl:value-of select="Issue"/></td>
<td></td>
</tr>
</xsl:template>
<xsl:template match="stamp">
<tr>
<td><xsl:value-of select="ItemNo"/></td>
<td><xsl:value-of select="Date"/></td>
<td><xsl:value-of select="Series"/></td>
<td><xsl:value-of select="Name"/></td>
<td><xsl:value-of select="Issue"/></td>
<td><xsl:value-of select="Minor"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
... and yes, the html isn't complete
来源:https://stackoverflow.com/questions/30609476/complex-grouping-using-xslt-1-0