Search code examples
xsltxslt-2.0

XSLT 2.0 using key with except returns unexpected result


NB: title changed to reflect the problem better.

My xml documents contain an element <tei:seg @type @xml:id @corresp> which wrap little 'stories'. The attribute @corresp allows me to connect these stories to a master story. For example, these seg are all connected by their @corresp:

doc1.xml//seg[@type='dep_event' @corresp='#JKL' @xml:id='doc1-05']
doc2.xml//seg[@type='dep_event' @corresp='#JKL' @xml:id='doc2-06']
doc6.xml//seg[@type='dep_event' @corresp='#JKL' @xml:id='doc6-03']

My objective is: when the XSLT template finds a @corresp, find other seg in other documents with the same @corresp and output their respective `@xml:id``

So, in the above example, if the current seg was @xml:id='doc1-05', the template outputs a list: Corresponds to doc2-06, doc6-03

Until I can solve the current problems with XSLT collection() in eXist-DB, I'm falling back on my previous solution: a 'TEI corpus' xml document which maintains a master list of all related tei-xml documents via xi:include. This way I provide a single document node whereby the processor can access and search all the xml documents.

So, I declare the corpus document:

<xsl:variable name="corpus" select="doc('ms609_corpus.xml')"/>

Then create a key for the @corresp:

<xsl:key name="correspkey" match="//tei:seg[@type='dep_event' and @corresp]" use="@corresp"/>

Then I use the key with the doc() to search:

<xsl:when test="tei:seg[@type='dep_event' and @corresp]">
     <xsl:variable name="correspvar" 
        select="data(self::seg[@type='dep_event' and @corresp]/@corresp)"/>
     <xsl:text>Corresponds to </xsl:text>
     <xsl:value-of select="data($corpus/(key('correspkey',$correspvar) except $correspvar)/@xml:id)" separator=", "/>
</xsl:when> 

It returns the results, but the except should exclude the current @corresp. Yet it is included in the results.


Solution

  • The except operator works on sequences of nodes based on node identity, see https://www.w3.org/TR/xpath20/#combining_seq defining

    The except operator takes two node sequences as operands and returns a sequence containing all the nodes that occur in the first operand but not in the second operand ... All these operators eliminate duplicate nodes from their result sequences based on node identity

    Based on that I think you simply want

     <xsl:value-of select="$corpus/(key('correspkey', current()/@corresp) except current())/@xml:id)" separator=", "/>
    

    Using data on nodes which atomizes nodes to values and then trying to use except which works on nodes doesn't seem to make sense to me.