Search code examples
xsltxslt-1.0xslt-2.0iterative-deepening

Deepening XSLT Structure using Attribute as Key


I've seen some variations of this quesiton asked here, but I'm not sure how to apply them to my situation, so I'm hoping maybe someone can help me here.

I have a flat XML file in a format similar to this one:

<item id="1"/>
<item id="1.1"/>
<item id="1.1.1"/>
<item id="1.1.2"/>
<item id="1.1.2.1"/>
<item id="1.2"/>
<item id="1.3"/>

I'm looking to set up the tags hierarchically based on the id attribute, like so:

<item id="1">
  <item id="1.1">
    <item id="1.1.1"/>
    <item id="1.1.2">
      <item id="1.1.2.1"/>
    </item>
  </item>
  <item id="1.2"/>
  <item id="1.3"/>
</item>

Some of the id values have two digit numbers (e.g., "1.2.3.15.1") which makes comparing them even more challenging.

Help?


Solution

  • Selecting the correct nodes can be tricky but as you have no hierarchy this works for your sample input (if you add a root element to it)

      <!-- start somewhere -->
      <xsl:template match="/root">
        <root>
          <!-- select all with no . in the id -->
          <xsl:apply-templates  select="//item[string-length(translate(@id,'1234567890',''))=0]" />
        </root>
      </xsl:template>
    
      <xsl:template match="item">
        <xsl:variable name="id" select="@id"/>
        <!-- how many . have we ? That is how deep we are and add 1 -->
        <xsl:variable name="deep" select="string-length(translate(@id,'1234567890',''))+1" />
        <xsl:copy>
          <!-- copy attribs over -->
          <xsl:apply-templates select="@*"/> 
          <!-- select all nodes that start with our curent id,  
               select nodes that are just one level below us 
               and don't select our selfs-->
          <xsl:apply-templates select="//item[starts-with(@id, $id) and string-length(translate(@id,'1234567890',''))=$deep and not(@id=$id)]"/>
        </xsl:copy>
      </xsl:template>
    
      <!-- copy attribs -->
      <xsl:template match="@*">
        <xsl:copy />
      </xsl:template>