1

I need some help here. I'm kinda new to XSLT.

I know in 2.0 you can use For-Each-Group which would solve my problem, but I'm limited to 1.0.

What I need to to group a flat XML using something like "group-starting-with" function.

This is only an example, but my real problem is very similar.

I have this XML:

<?xml version="1.0" encoding="UTF-8"?>
    <catalog>

        <xpto name="1">ABC</xpto>
        <title>Empire Burlesque</title>
        <artist>Bob Dylan</artist>
        <country>USA</country>
        <company>Columbia</company>
        <price>10.90</price>
        <year>1985</year>
        <xpto name="2">ABC</xpto>

        <xpto name="1">ABC</xpto>
        <title>Hide your heart</title>
        <artist>Bob Dylan</artist>
        <country>UK</country>
        <company>CBS Records</company>
        <price>9.90</price>
        <year>1988</year>
        <xpto name="2">ABC</xpto>

    </catalog>

And I want it to be:

<?xml version="1.0" encoding="UTF-8"?>
    <catalog>

        <group>
            <xpto name="1">ABC</xpto>
            <title>Empire Burlesque</title>
            <artist>Bob Dylan</artist>
            <country>USA</country>
            <company>Columbia</company>
            <price>10.90</price>
            <year>1985</year>
            <xpto name="2">ABC</xpto>
        </group>

        <group>
            <xpto name="1">ABC</xpto>
            <title>Hide your heart</title>
            <artist>Bob Dylan</artist>
            <country>UK</country>
            <company>CBS Records</company>
            <price>9.90</price>
            <year>1988</year>
            <xpto name="2">ABC</xpto>
        </group>

    </catalog>

So I want to group the elements every time the following appears:

    <xpto name="1">ABC</xpto>

Is there any way to do this with XSLT 1.0?

Thank you very much!

Kaisers
  • 15
  • 3

1 Answers1

2

Assuming you want to group the elements starting with <xpto name="1"> elements, you could define a key to group the other child elements by the first such element that precedes them:

 <xsl:key name="start" match="*[not(self::xpto[@name='1'])]" use="generate-id(preceding-sibling::xpto[@name='1'][1])" />

Then, you can select all your starting elements, and get the other group items like so:

<xsl:apply-templates select=".|key('start', generate-id())" /> 

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:key name="start" match="*[not(self::xpto[@name='1'])]" use="generate-id(preceding-sibling::xpto[@name='1'][1])" />

  <xsl:output method="xml" indent="yes" />

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="catalog">
    <xsl:copy>
      <xsl:for-each select="xpto[@name='1']">
        <group>
          <xsl:apply-templates select=".|key('start', generate-id())" /> 
        </group>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
Tim C
  • 70,053
  • 14
  • 74
  • 93
  • Dear Tim C, thank you very much. It solved my problem!! I can't vote because my profile is new and stackoverflow doesn't allow :( – Kaisers May 16 '19 at 10:18
  • @Kaisers : You can close the question by accepting the answer – Vebbie May 16 '19 at 10:21
  • After playing with it after a bit I need you help again. After adding the group tag I need to loop each group and get the value of artist. How can I manage to do that? I'm struggling with the template logic... – Kaisers May 16 '19 at 11:39
  • 1
    @Kaisers, ask a new question with the necessary details. In your original question you wanted to output all elements of the group and Tim has shown you how to do that (and how to select all items in the group with `.|key('start', generate-id())`. It is not clear why you think you need to "loop" or where/how you need that particular value. Ask a new question explaining that. – Martin Honnen May 16 '19 at 11:46