Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT sorting and grouping involving attributes

I have an XML file that looks like this example:

<Data>
  <defect>
    <record-id>1</record-id>
    <custom-field-value field-name="Release Version" field-value="1.0"/>
    <custom-field-value field-name="Other info" field-value=""/>
    <custom-field-value field-name="More info" field-value="blah"/>

    <event include-in-release-notes="yes">
      <notes>This is a release note to include</notes>
    </event>

    <event include-in-release-notes="no">
      <notes>This is not a release note</notes>
    </event>
  </defect>

  <defect>
    <record-id>2</record-id>
    <custom-field-value field-name="Release Version" field-value="1.5"/>
    <custom-field-value field-name="Other info" field-value=""/>
    <custom-field-value field-name="More info" field-value="blah"/>

    <event include-in-release-notes="yes">
      <notes>This is a release note to include for 1.5</notes>
    </event>

    <event include-in-release-notes="no">
      <notes>This is not a release note</notes>
    </event>
  </defect>
</Data>

What I'm trying to do is create a release notes document that first sorts and finds all of the unique @field-value values of the elements that have the @field-name equal to "Release Version". There may be other elements that do not pertain to release version. Here is the output I'm looking for:

Release Version: 1.0
  o This is a release note to include
Release Version: 1.5
  o This is a release note to include for 1.5
Release Verison: x.x
  o one release note
  o another release note

I've read up a bunch about the "Muenchian" method and sorting and grouping, but I'm struggling with the fact that I have attributes I need to compare to. Most of the examples I've read discuss sorting on elements which seems much more intuitive. And I need to find and sort on multiple attributes, well, my head just starts to explode.

I've come up with stylesheets that will give me ALL of the elements that have "Release Version" as the text using:

<xsl:key name="keyMajorReleases" match="custom-field-value" use="@field-name"/>
<xsl:for-each select=key('keyMajorReleases', 'Release Version')">
  <xsl:sort order="descending" data-type="text" select="@field-value"/>

But that gives them ALL to me, not just the unique ones. And then I haven't figured out how to get the 'event' element that has the release note I need to print.

When I've tried to use 'generate-id()', I only get ONE result, as I guess there is only ONE unique entry with my value to find:

<xsl:for-each select="//custom-field-value[generate-id(.)=generate-id(key('keyMajorReleases', 'Release Version')[1])]">
like image 347
Gunn Avatar asked Nov 03 '22 08:11

Gunn


1 Answers

With your current key...

<xsl:key name="keyMajorReleases" match="custom-field-value" use="@field-name"/>

You would be grouping custom-field-value elements by field-name, which you would do if you wanted to find the distinct field-name values. However, you want to group by the field value attributes for 'Release Versions'. This means you need to define your key like so:

<xsl:key name="keyMajorReleases" match="custom-field-value[@field-name='Release Version']" use="@field-value"/>

With this key, you are matching only on custom-field-value where the field name is 'Release Version'. You can then use this to get the distinct field values attributes, like so:

<xsl:apply-templates 
     select="defect/custom-field-value[@field-name='Release Version']
         [generate-id()=generate-id(key('keyMajorReleases', @field-value)[1])]" />

Then to get your release notes for a given release version, you could then use the key again

<xsl:apply-templates 
   select="key('keyMajorReleases', @field-value)
        /following-sibling::event[@include-in-release-notes='yes']" />

Here is the full XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="keyMajorReleases" match="custom-field-value[@field-name='Release Version']" use="@field-value"/>
   <xsl:template match="/Data">
      <ul>
         <xsl:apply-templates select="defect/custom-field-value[@field-name='Release Version'][generate-id()=generate-id(key('keyMajorReleases', @field-value)[1])]"/>
      </ul>
   </xsl:template>

   <xsl:template match="custom-field-value">
      <li>Release Version: <xsl:value-of select="@field-value"/>
         <ul>
            <xsl:apply-templates select="key('keyMajorReleases', @field-value)/following-sibling::event[@include-in-release-notes='yes']"/>
         </ul></li>
   </xsl:template>

   <xsl:template match="event">
      <li>
         <xsl:value-of select="notes"/>
      </li>
   </xsl:template>
</xsl:stylesheet>

When applied to your sample XML, the following is output

<ul>
   <li>Release Version: 1.0
       <ul>
          <li>This is a release note to include</li>
       </ul>
   </li>
   <li>Release Version: 1.5
      <ul>
         <li>This is a release note to include for 1.5</li>
      </ul>
   </li>
</ul>
like image 114
Tim C Avatar answered Dec 17 '22 14:12

Tim C