1
votes

XSLT 2.0 processing problem with key values and apply-template.

Source xml

<?xml version="1.0" encoding="UTF-8"?>
<ELEMENTS>
  <EA-NUMERICAL ID="MyFloat_20_62045">
        <NAME>MyFloat</NAME>
  </EA-NUMERICAL>
  <RANGEABLE-VALUE-TYPE ID="PercentType_20_62177">
        <NAME>PercentType</NAME>
        <BASE-RANGEABLE-REF TYPE="EA-NUMERICAL">/MyFloat_20_62045</BASE-RANGEABLE-REF>
  </RANGEABLE-VALUE-TYPE>
  <RANGEABLE-VALUE-TYPE ID="TorqueType_20_62239">
        <NAME>TorqueType</NAME>
        <BASE-RANGEABLE-REF TYPE="EA-NUMERICAL">/MyFloat_20_62045</BASE-RANGEABLE-REF>
  </RANGEABLE-VALUE-TYPE>
</ELEMENTS>

XSLT's key definition for reporting the RANGEABLE-VALUE-TYPE:

<xsl:key name="by-rangeable-value-type" match="RANGEABLE-VALUE-TYPE" use="BASE-RANGEABLE-REF"/>
<xsl:key name="ea-numerical-type" match="EA-NUMERICAL" use="@ID"/>

And then I have two templates (first one for full information and another one for the case when EA-NUMERICAL has been reported earlier):

<!-- Reporting the full information -->
<xsl:template match="RANGEABLE-VALUE-TYPE[. is key('by-rangeable-value-type', BASE-RANGEABLE-REF)[1]]">
</xsl:template>

<!-- Reporting the reference -->
<xsl:template match="RANGEABLE-VALUE-TYPE[not(. is key('by-rangeable-value-type', BASE-RANGEABLE-REF)[1])]">
<object>
   <xsl:attribute name="href">#<xsl:value-of select="tokenize(BASE-RANGEABLE-REF, '/')[last()]"/></xsl:attribute>
</object>
</xsl:template>

Template call:

<xsl:apply-templates select="key('ea-numerical-type', tokenize(BASE-RANGEABLE-REF, '/')[last()])"/>

Problem is if the EA-NUMERICAL is be reported elsewhere/earlier from another template.

Now during the 1st RANGEABLE-VALUE-TYPE processing it will be reported fully again (by utilizing the first of the templates, like the key definition states). For the 2nd RANGEABLE-VALUE-TYPE, it uses the reference template correctly.

But is there a way in XSLT 2.0 to create/combine a key with several different element or attribute values? (In this sample combination of tokenized BASE-RANGEABLE-REF values and EA-NUMERICAL id attribute value?)

1
Please say more clearly what the error or problem is. Sometimes this is easier if you show the difference between the XML output you get and the one you expect. - Mathias Müller
Problem: now first of the two "Reporting full information"-template will be called in every execution, when I execute the Template call (shown at the bottom). But if the source XML's EA-NUMERICAL has been processed earlier (it exists already in the output stream), I would expect that is should call the second "Reporting the reference"-template and a reference would be created the the existing instance. Does this make the my problem more clear? - Janne
So, you'd like to check whether an element is already present in the output stream? - Mathias Müller
Yes, exactly. Big thanks for help Ian and others, very useful tip! - Janne
If Ian's answer was helpful to you, please accept his answer (mark the tick on the left). - Mathias Müller

1 Answers

2
votes

You could change the key definition to include both by using

<xsl:key name="by-rangeable-value-type"
         match="RANGEABLE-VALUE-TYPE/BASE-RANGEABLE_REF | EA-NUMERICAL/@ID"
         use="tokenize(., '/')[last()]"/>

Now the key set for a given ID will be a mixture of ID attribute nodes and BASE-RANGEABLE-REF element nodes. To check whether a given RANGEABLE-VALUE-TYPE is the first mention you could use something like

<xsl:template match="RANGEABLE-VALUE-TYPE[BASE-RANGEABLE-REF intersect
  key('by-rangeable-value-type', tokenize(BASE-RANGEABLE-REF, '/')[last()])[1]]">

which will match if this is the first matching BASE-RANGEABLE-REF and there is no EA-NUMERICAL before it.

You'll then also need two modes for the "report full information" template, so you can map the EA-NUMERICAL to a cross-reference if it has already been reported in full for a RANGEABLE-VALUE-TYPE

<xsl:template match="EA-NUMERICAL" mode="copy" name="copy-ean">
  <!-- whatever you do to report the full details -->
</xsl:template>

<!-- first mention (i.e. not yet mentioned in any RANGEABLE-VALUE-TYPE) -->
<xsl:template match="EA-NUMERICAL[@ID is key('by-rangeable-value-type', @ID)[1]]">
  <xsl:call-template name="copy-ean" />
</xsl:template>

<!-- other mentions becom href -->
<xsl:template match="EA-NUMERICAL">
  <object href="#{@ID}" />
</xsl:template>

and in your "first RANGEABLE-VALUE-TYPE" template use

<xsl:apply-templates mode="copy" select="key('ea-numerical-type', tokenize(BASE-RANGEABLE-REF, '/')[last()])"/>