Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

concat(teachers/teacher, ':', keywords/keyword) will concat first teachers/teacher, ':', and first keywrods/keyword for every training_center/course. But in your second "course" AJAX is the first "keyword".

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top