I have the following XML code:
<training_center>
<course id="XML">
<title>Course 1</title>
<keywords>
<keyword>XML</keyword>
<keyword>XPath</keyword>
</keywords>
<teachers>
<teacher>Nikitin</teacher>
<teacher>Pavlov</teacher>
</teachers>
</course>
<course id="AJAX">
<title>Course 2</title>
<keywords>
<keyword>AJAX</keyword>
<keyword>XML</keyword>
</keywords>
<teachers>
<teacher>Nikitin</teacher>
<teacher>Chebykin</teacher>
</teachers>
</course>
</training_center>
And the following XSL code:
<xsl:key
name='concat_key'
match="/training_center/course"
use="concat(teachers/teacher, ':', keywords/keyword)"
/>
<xsl:template match="/">
<xsl:apply-templates select="key('concat_key', concat('Nikitin',':','XML'))" />
</xsl:template>
<xsl:template match="course">
<xsl:element name="course">
<xsl:value-of select="./title" />
</xsl:element>
</xsl:template>
After XSL-transformation Course 1 is shown only. But logically Course 2 shall be shown also.
In what error?
I am assuming you are using XSLT 1.0, because ... [an educated guess based on your result].
First, the reason why your method cannot work is that the concat()
function works on string-values, not node-sets.
concat(teacher, ':', keyword)
is evaluated as:
concat(string(teacher), ':', string(keyword))
and (in XSLT 1.0) string(node-set)
returns the string-value of the first node in the set.
There are other methods you can use to achieve your goal, for example:
1. Use a key with a predicate:
<xsl:key name="course-by-teacher" match="course" use="teachers/teacher" />
<xsl:template match="/">
<output>
<xsl:apply-templates select="key('course-by-teacher', Nikitin')[keywords/keyword='XML']" />
</output>
</xsl:template>
<xsl:template match="course">
<course>
<xsl:value-of select="title" />
</course>
</xsl:template>
2. Use the intersection of two keys:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:set="http://exslt.org/sets"
extension-element-prefixes="set">
<xsl:output method='xml' version='1.0' encoding='utf-8' indent='yes'/>
<xsl:key name="course-by-teacher" match="course" use="teachers/teacher" />
<xsl:key name="course-by-keyword" match="course" use="keywords/keyword" />
<xsl:template match="/">
<output>
<xsl:apply-templates select="set:intersection(key('course-by-teacher', 'Nikitin'), key('course-by-keyword', 'XML'))" />
</output>
</xsl:template>
<xsl:template match="course">
<course>
<xsl:value-of select="title" />
</course>
</xsl:template>
</xsl:stylesheet>
Note that this method requires the EXSLT set:intersection()
function which is supported by many XSLT 1.0 processors - but not all of them. If necessary, I will post a (more complex) method that can produce the intersection of two node-sets natively in XSLT 1.0.