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()])"/>