Search code examples
xsltxpathxsl-fo

Find value of XPath in different object in array


I'm currently building a document in XSL-FO to build a table based on a list of items. The issue is that the items have relationships between them, and I need to be able to reference a value from the other item based on the relationship.

Say I had an input object like:

    <Products>
      <Product>
        <ID>A</ID>
        <Name>Cat</Name>
        <Relationship>
          <ID>B</ID>
        </Relationship>
      </Product>
      <Product>
        <ID>B</ID>
        <Name>Hat</Name>
      </Product>
    </Products>

I need to be able to put together a table that has the format of:

    Name
    ----
    Cat
     - Hat
    ----
    Hat

To build the table rows, I've already done

    <fo:table>
      <xsl:apply-templates select='Product' />
    </fo:table>

and then 'within' each Product, putting a block based on the name:

    <fo:block>
      <xsl:value-of select="Name" />
    </fo:block>
    <fo:block>
      <xsl:apply-template select="..." />
    </fo:block>

My issue is the ... select option to get the name. I was hoping to be able to build an xpath along the lines of ../Product[ID=./Relationship/ID]/Name but it doesn't work because the ./ now refers to any of the products, not just the "starting" object.

Is there a way to accomplish this referencing using xpath?


Solution

  • XSLT has a built-in key mechanism to resolve cross-references. Start by defining a key at the top level of your stylesheet as:

    <xsl:key name="product" match="Product" use="ID" />
    

    Then, from the context of Product, you can do:

    <xsl:apply-templates select="key('product, Relationship/ID)/Name"/>
    

    Alternatively, you could do:

    <xsl:apply-templates select="../Product[ID=current()/Relationship/ID]/Name"/>
    

    But using a key is both more elegant and more efficient.