Rhiannon
Rhiannon

Reputation: 93

Converting text to cross-reference in XSL

I've seen a lot of information on how to break down XML cross-references in XSL (for example XSL cross-reference). I'm completely stuck on how to do the opposite. I don't even know what it's technically called, so I don't know where to look to find out.

Given the XML

<shoes>
  <shoe>
    <colour>brown</colour>
    <make>Shoeco</make>
  </shoe>
  <shoe>
    <colour>black</colour>
    <make>Shoeco</make>
  </shoe>
  <shoe>
    <colour>purple</colour>
    <make>Footfine</make>
  </shoe>
  <shoe>
    <colour>brown</colour>
    <make>Footfine</make>
  </shoe>
  <shoe>
    <colour>blue</colour>
    <make>Leathers</make>
  </shoe>
</shoes>

I want the output

<inventory>
  <shoelist>
    <item>
      <colour>brown</colour>
      <shopref>1</shopref>
    </item>
    <item>
      <colour>black</colour>
      <shopref>1</shopref>
    </item>
    <item>
      <colour>purple</colour>
      <shopref>1</shopref>
    </item>
    <item>
      <colour>brown</colour>
      <shopref>2</shopref>
    </item>
    <item>
      <colour>blue</colour>
      <shopref>2</shopref>
    </item>
  </shoelist>
  <shoeshops>
    <shop>
      <refno>1</refno>
      <name>ShoeCo</name>
    </shop>
    <shop>
      <refno>2</refno>
      <name>FootFine</name>
    </shop>
    <shop>
      <refno>3</refno>
      <name>Leathers</name>
    </shop>
  </shoeshops>
</inventory>

How can I (a) create a list of each unique shoe shop, with an incrementing ID number, and (b) reference the correct shoeshop by ID number in each shoe element?


Upvotes: 0

Views: 51

Answers (1)

Michael Kay
Michael Kay

Reputation: 163575

I would first build the list of shoeshops in a variable:

<xsl:variable name="shops">
  <shoeshops>
    <xsl:for-each-group select="shoe" group-by="make">
      <shop>
        <refno>{position()}</refno>
        <name>{current-grouping-key()}</name>
      </shop>
    </xsl:for-each-group>
  </shoeshops>
</xsl:variable>

Then create the shoelist:

<xsl:mode on-no-match="shallow-copy"/>
<inventory>
   <shoelist>
     <xsl:apply-templates select="shoes/shoe"/>
   </shoelist>
   <xsl:copy-of select="$shops"/>
</inventory>

<xsl:template match="make">
  <shopref>{$shops//shop[name="current()"]/refno}</shopref>
</xsl:template> 

This uses some XSLT 3.0 constructs for brevity. Converting to XSLT 2.0 is fairly easy, converting to XSLT 1.0 is much harder.

Upvotes: 2

Related Questions