Register  Login       Search  
Forum  
 
  
 
Forum  Forum     
 
SearchForum Home
  XML  XSLT, XPath and XQuery  document() and ...
 document() and key function usage
 
CNemo
5 posts
Joined
7/16/2006

document() and key function usage
Posted: 16 Jul 06 7:08 PM (N/A) Modified By CNemo  on 7/16/2006 7:08:56 PM)
Hello friends!
I have problems to use document() and key functions. Basically what i need to do is to split one data streem into several other, according to some kind of lookups. Following is what i've accomplished so far. Simplified sample.

Data.
<data>
    <elements>
        <element name="bear"/>
        <element name="deer"/>
        <element name="shark"/>
        <element name="snake"/>
        <element name="cat"/>
        <element name="frog"/>
        <element name="carp"/>
        <element name="catfish"/>
    </elements>
</data>


Categories
<categories>
    <category name="mammal">
        <element name="bear"/>
        <element name="cat"/>
    </category>
    <category name="reptile">
        <element name="frog"/>
        <element name="pony"/>
    </category>
    <category name="fish">
        <element name="catfish"/>
        <element name="shark"/>
    </category>
</categories>


Transformation
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

<xsl:key name="animal-name" match="/data/elements/element" use="@name" />

<xsl:template match="/data">
    <table border="1">
        <tbody>
        <xsl:for-each select="document('categories.xml')/categories/category">
            <tr  bgcolor="silver">
                <td><xsl:value-of select="@name" />
                </td>
            </tr>
            <xsl:for-each select="element">
                <tr><td>
                   <xsl:apply-templates select="key('animal-name', @name)" mode="test"/>
                     </td>
                 </tr>
            </xsl:for-each>
        </xsl:for-each>   
        </tbody>
    </table>
</xsl:template>

<xsl:template match="/data/elements/element" mode="test">
    <xsl:value-of select="@name" />
</xsl:template>

</xsl:stylesheet>



It does work as expected, until string with key() call, but last template is not called.
If i embed categories into data file everything works just fine.

I use .NET XslTransform class. It might be very, stupid question, but sorry, i need your help, because my experiance is very limited in this area. Any advise will be greatly appreciated!
olegt
85 posts
www.xmllab.net
Joined
2/25/2005

Re: document() and key function usage
Posted: 17 Jul 06 11:38 AM (N/A) Modified By olegt  on 7/17/2006 11:40:48 AM)

The key to your problem is as follows: key() function selects matching nodes from a document the context node belongs to. In your case it's /categories/category/element element. If you want to select nodes from original document you need to switch context. Usually it's done using xsl:for-each:

<xsl:variable name="root" select="/"/>

<xsl:for-each select="document('categories.xml')/categories/category">
            <tr  bgcolor="silver">
                <td><xsl:value-of select="@name" />
                </td>
            </tr>
            <xsl:for-each select="element">
                <tr><td>

                   <xsl:variable name="element-name" select="@name"/>

                  <xsl:for-each select="$root">
                       <xsl:apply-templates select="key('animal-name', $element-name)" mode="test"/>

                 </xsl:for-each>
                     </td>
                 </tr>
            </xsl:for-each>
        </xsl:for-each>

 


Oleg Tkachenko, Microsoft MVP for XML, MCPD
http://www.XmlLab.Net | http://blog.tkachenko.com
CNemo
5 posts
Joined
7/16/2006

Re: document() and key function usage
Posted: 17 Jul 06 7:21 PM (N/A) Modified By CNemo  on 7/17/2006 7:24:40 PM)
Oleg,

thanks a lot! I realised it myself this morning.
But got another question.

Is there any performance penalties when variables used this way? Is performance affected by nodeset size?
I just noticed that almost any sample for similar situation uses select="/", but in this case nodeset might be potentialy very big.
olegt
85 posts
www.xmllab.net
Joined
2/25/2005

Re: document() and key function usage
Posted: 18 Jul 06 1:58 AM (Israel)
There is no performance penalty here. "/" selects just single root node, so this nodeset contains just  one node.
Oleg Tkachenko, Microsoft MVP for XML, MCPD
http://www.XmlLab.Net | http://blog.tkachenko.com
CNemo
5 posts
Joined
7/16/2006

Re: document() and key function usage
Posted: 24 Jul 06 7:58 AM (United States)
Oleg,

taking in account sample data, is there any way to create additional misc category for animals which do not belong to any other category?
olegt
85 posts
www.xmllab.net
Joined
2/25/2005

Re: document() and key function usage
Posted: 26 Jul 06 7:26 AM (Israel)

Sure, but then you need to iterate over source elements, not categories - just select each element having no category:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> <xsl:key name="animal-name" match="/data/elements/element" use="@name" /> <xsl:template match="/data"> <table border="1"> <tbody> <xsl:variable name="root" select="/"/> <xsl:variable name="categories" select="document('source2.xml')/categories/category"/> <xsl:for-each select="$categories"> <tr bgcolor="silver"> <td> <xsl:value-of select="@name" /> </td> </tr> <xsl:for-each select="element"> <tr> <td> <xsl:variable name="element-name" select="@name"/> <xsl:for-each select="$root"> <xsl:apply-templates select="key('animal-name', $element-name)" mode="test"/> </xsl:for-each> </td> </tr> </xsl:for-each> </xsl:for-each> <tr bgcolor="silver"> <td>Misc</td> </tr> <xsl:for-each select="elements/element[not(@name=$categories/element/@name)]"> <tr> <td> <xsl:apply-templates select="." mode="test"/> </td> </tr> </xsl:for-each> </tbody> </table> </xsl:template> <xsl:template match="/data/elements/element" mode="test"> <xsl:value-of select="@name" /> </xsl:template> </xsl:stylesheet>


Oleg Tkachenko, Microsoft MVP for XML, MCPD
http://www.XmlLab.Net | http://blog.tkachenko.com
CNemo
5 posts
Joined
7/16/2006

Re: document() and key function usage
Posted: 26 Jul 06 2:21 PM (United States)
Thanks!

I've got idea.
But, You know what? I was under impression that there is not SQL IN predicate equivalent in XSLT. Not, I see that I was wrong.
not[@name=$/categories/element/name].

Is there any reason to use key and for-each then?
CNemo
5 posts
Joined
7/16/2006

Re: document() and key function usage
Posted: 28 Jul 06 11:16 AM (United States)
I also got the problem related to the casesensitivity.

The data i'm getting from provider is case sensitive. So, once i have wrong case i fail comparison. I've tried to use translate() in not(@name = nodeset) condition, but it gives me exception when i try to apply it to the node set.

something like this elemens/element/translate(@name, '...', '...')

It's definitly looks weird, because there is no 'translate' elements in the data, but i simply dont know how to express it.

Is there any way to work around this problem?

  XML  XSLT, XPath and XQuery  document() and ...
Forum Home  Search