Complex Grouping using XSLT 1.0

你离开我真会死。 提交于 2019-12-13 06:51:52

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!