0

I am aware that there are plenty of questions (and answers) here on the lookup from external xml files via xslt. However, I still haven't got my head around the logic of the key function so I'm having a hard time applying other solutions to my use case.

I have two xml files:

versA.xml

<TEI>
  <div>
    <l id="A001" corresp="B001">First line of VersA</l>
    <l id="A002" corresp="B002">Second line of VersA</l>
    <l id="A003" corresp="B003">Third line of VersA</l>
  </div>
</TEI>

and

versB.xml

<TEI>
  <div>
    <l id="B001" corresp="A001">First line of VersB</l>
    <l id="B002" corresp="A002">Second line of VersB</l>
    <l id="B003" corresp="A003">Third line of VersB</l>
  </div>
</TEI>

The files refer to each other via the corresp-attribute.

I'm trying to figure out a xsl stylesheet (trans.xsl) that parses versA.xml, prints its own text node and then looks up the corresponding text node in versB.xml

trans.xsl

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:variable name="vB" select="document('versB.xml')/TEI/div/l"/>

<xsl:template match="/TEI/div/l">
I found ID <xsl:value-of select="@id"/> in versA.xml.
How can I get the corresponding node in versB.xml which has the ID <xsl:value-of select="@corresp"/>?
</xsl:template>


</xsl:stylesheet>

What I can do is output the ids of versA.xml and access versB.xml. I find it extremely difficult however, to set up an appropriate key function that takes the corresp value from versA.xml to look up the corresponding id in versB.xml

I'd be happy if somebody could explain how this can be achieved.

For compatibility reasons xslt version 1.0 would be preferred.

I have updated my stylesheet according to the suggestions given in the comments. The following gives the desired output:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:key name="ref" match="TEI/div/l" use="@id"/>

<xsl:template match="/TEI/div/l">

  <xsl:variable name="corresp" select="@corresp"/>
  <xsl:value-of select="."/> corresponds to 
  <xsl:for-each select="document('versB.xml')">
     <xsl:value-of select="key('ref', $corresp)"/>
   </xsl:for-each>

</xsl:template>

</xsl:stylesheet>
Alex W.
  • 119
  • 9

1 Answers1

1

You need to declare a key with xsl:key, giving it a name you can choose, using a match pattern of the nodes you want to "index", you want to declare the key for, and defining a key value with the use attribute, an XPath expression:

<xsl:key name="ref" match="TEI/div/l" use="@id"/>

To use a key you call the key function with the name of the declared key as the first argument, the key value as the second argument and, in XSLT 2 or later, the document or subtree you want to search, as the third argument: key('ref', @corresp, document('versB.xml')).

If you are really restricted to XSLT 1 then, with two different documents, as the third argument of the key function is not supported, you need to switch the "context document" with for-each, for instance:

<!-- need to store value of main input document we want to lookup in variable -->
<xsl:variable name="corresp" select="@corresp"/>

<!-- now we change the context document to be able to apply the key function on secondary input -->
<xsl:for-each select="document('versB.xml')">
   <xsl:value-of select="key('ref', $corresp)"/>
</xsl:for-each>

Both example uses of the key function above assume you make them in the context of an xsl:template match="/TEI/div/l" so that @corresp selects the value of that attribute of an l element in your main input.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Thanks! I've updated my code in my questions above. The key function seems not to work as it output an empty string. – Alex W. Mar 07 '20 at 16:09
  • 1
    @AlexW., see the edit, it seems I have misunderstood the relation, if the `corresp` in the first document refers to the `id` in the second then the `use` attribute in the key declaration needs to be `use="@id"`. – Martin Honnen Mar 07 '20 at 16:21
  • Just to add another difficulty (relevant to my use case): The `` text nodes sometimes contain `` elements. I can define templates that transform the ``-elements in the main document (**versA.xml**). I don't know, however, how to apply-templates to the 'imported' **versB.xml**. – Alex W. Mar 07 '20 at 16:55
  • @AlexW., please raise that as a separate question unless `` (XSLT 1, needs to be inside of the `for-each` that changed the context document) or `` (XSLT 2 and later) does not help. – Martin Honnen Mar 07 '20 at 17:43