Search code examples
xsltgroupingxslt-2.0

Combining nodes from separate levels into a single list in XSLT 2.0


What I am trying to do is take a specific node, in the example <person>, and combine it into a list of all the person nodes available. Here is my sample source:

   <group groupName="The Little Rascals">
      <peopleInGroup>
            <people>
               <person>
                  <name value="John Doe">
                  <birthdate value="01/01/1953">
               </person>
               <person>
                  <name value="John Doe 2">
                  <birthdate value="01/01/1953">
               </person>
               <childrenInGroup>
                  <person>
                      <name value="Jane">
                      <birthdate value="01/01/1973">
                  </person>
                  <person>
                      <name value="Suzie">
                      <birthdate value="01/01/1970">
                  </person>
                </childrenInGroup>
             </people>
         </peopleInGroup>
      </group>

In this case what I am wanting to do is get a list of all the <person> elements regardless of level and loop over each one. The list would look something like this:

               <person>
                  <name value="John Doe">
                  <birthdate value="01/01/1953">
               </person>
               <person>
                  <name value="John Doe 2">
                  <birthdate value="01/01/1953">
               </person>
               <person>
                   <name value="Jane">
                   <birthdate value="01/01/1973">
               </person>
               <person>
                   <name value="Suzie">
                   <birthdate value="01/01/1970">
               </person>

The only sorting I would want to do in this case might be by Birthday. I was thinking that something like a Deep Copy of the <person> node, but I do not know what the implementation of that would look like.

Once the list is created, the idea would be to loop over the list in a for-each like this:

<xsl:for-each select="$persons">
   <xsl:value-of select="@name"/>
   <xsl:value-of select="@birthday"/>
</xsl:for-each>

Thanks for the help!


Solution

  • Apart from the sorting you can simply use XPath with //person to select all person elements as a sequence.

    If you want to sort them you can use xsl:perform-sort:

      <xsl:variable name="sorted-persons" as="element(person)*">
          <xsl:perform-sort select="descendant::person">
              <xsl:sort select="xs:date(replace(birthdate/@value, '([0-9]{2})/([0-9]{2})/([0-9]{4})', '$3-$2-$1'))"/>
          </xsl:perform-sort>
      </xsl:variable> 
    

    You can then use that sorted sequence in a for-each if you want or directly output the data using value-of: https://xsltfiddle.liberty-development.net/94hvTyZ