Search code examples
xsltxslt-2.0tokenize

XSLT 2.0 3.0 for-each context error when tokenizing attributes


Given this XML

<dmodule>   
      <content>     
            <warningsAndCautionsRef>
                  <warningRef id="w001" warningIdentNumber="warning-001">
                  </warningRef>
                  <warningRef id="w002" warningIdentNumber="warning-002">
                  </warningRef>
                  <cautionRef id="c001" cautionIdentNumber="caution-001">
                  </cautionRef>
                  <cautionRef id="c002" cautionIdentNumber="caution-002">
                  </cautionRef>
            </warningsAndCautionsRef>
            <faultReporting>
                  <preliminaryRqmts>
                        <reqSafety>
                              <safetyRqmts cautionRefs="c001 c002" warningRefs="w001 w002"/>
                        </reqSafety>
                  </preliminaryRqmts>
            </faultReporting>
      </content>
</dmodule>
     

I would like to tokenize the attributes @cautionRefs (and @warningRefs) and then find the cautionRef element that matches its @id to the tokenized value:

<xsl:template match="@cautionRefs">
    <xsl:for-each select="tokenize(.,'\s')">
      <xsl:apply-templates select="//*[@id=.]"/>
    </xsl:for-each>
</xsl:template>

but the apply-templates fails: Fatal error during transformation Leading '/' selects nothing: the context item is not a node. It works if I don't tokenize and use string functions instead but that is not desirable.

Desired result: Tokenize @cautionRefs="c001 c002" (which has multiple parent elements)

So each value is passed to the <cautionRef>template that will retrieve the caution and warning statements, to be displayed in a PDF:

<xsl:apply-templates select="//*[@id='c001']"/>
<xsl:apply-templates select="//*[@id='c002']"/> 

I tried using <xsl:key name="id" match="*" use="@id"/> with

<xsl:for-each select="key('id',tokenize(.,'\s'))">

but the for-each is blank.

The above apply-templates will match with this <cautionRef> template, which retrieves the caution and warning statements correctly. I just need help with the context of the @cautionRefs template:

<xsl:template match="cautionRef">
    <xsl:variable name="IdentNumber" select="@cautionIdentNumber"/>
    <xsl:apply-templates select="//cautionSpec[cautionIdent/@cautionIdentNumber=$IdentNumber]"/>
  </xsl:template>

Solution

  • Here's a full working example. NB it's best to have this level of detail in the actual question; i.e. a sample input file, the XSLT, and output, along with an example of what you want the output to look like.

    Input:

    <test>
      <safetyRqmts cautionRefs="c001 c002" warningRefs="w001"/>
      <cautionRef id="c001" cautionIdentNumber="caution-001"/>
      <cautionRef id="c002" cautionIdentNumber="caution-001"/>
    </test>   
    

    Stylesheet:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="2.0">
      
    <xsl:template match="*|@*">
      <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates/>
      </xsl:copy>
    </xsl:template>
    
    <xsl:key name="by_id" match="*[@id]" use="@id"/>
    
    <xsl:template match="@cautionRefs|@warningRefs">
        <xsl:apply-templates select="key('by_id', tokenize(.))"/>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Output:

    <test>
      <safetyRqmts><cautionRef id="c001" cautionIdentNumber="caution-001"/><cautionRef id="c002" cautionIdentNumber="caution-001"/></safetyRqmts>
      <cautionRef id="c001" cautionIdentNumber="caution-001"/>
      <cautionRef id="c002" cautionIdentNumber="caution-001"/>
    </test>