5,666,979 members and growing! (16,728 online)
Email Password   helpLost your password?
Languages » XML » XSLT     Intermediate

Adding custom XPath functions to XSL

By Stefan Popov

This article shows you how to add context aware XPath functions to an XSL transformation. This is done by a hack using reflection.
C#, XML.NET 1.0, Win2K, WinXP, Windows, .NETVisual Studio, VS.NET2002, Dev

Posted: 2 Mar 2003
Updated: 2 Mar 2003
Views: 45,954
Bookmarked: 11 times
Announcements
Loading...



Search    
Advanced Search
Sitemap
9 votes for this Article.
Popularity: 3.58 Rating: 3.75 out of 5
1 vote, 11.1%
1
1 vote, 11.1%
2
0 votes, 0.0%
3
2 votes, 22.2%
4
5 votes, 55.6%
5

Introduction

The XSL transformation is a powerful way of visualizing XML. The presence of extension objects makes it even more powerful. However it turns out that extension objects are very limited. They have no way of retrieving the current context of the transformation or the current document. Furthermore their return type is limited to only numbers and strings (returning an IXPathNavigable is restricted. See here for more details). This article presents a work around for the first problem.

Why use context aware XPath functions

A simple but very powerful application of such is the eval function. It evaluates XPath queries contained in a string or variable. For example imagine you have the following:

<xsl:variable name='vTest'>123456</xsl:variable>
<xsl:variable name= 'vQuery'>$vTest</xsl:variable>

If you take the value of vQuery using xsl:value-of you would get $vTest as a result. On the other hand <xsl:value-of select='eval($vTest)'/> will evaluated to 123456. There is no such function built-in so we will write it our selves.

Background

To read this article you should have good knowledge in C# and the Microsoft's .NET XML parser.

How it works

It looks like Microsoft have had the idea of allowing the user to write context aware functions. Have a look at the 2 classes IXsltContextFunction and XsltContext in the System.Xml.Xsl namespace. You can make your own context and function classes derived from the above 2, but you can use them only in programmatic XPath queries. An article I found in Microsoft's KB "recomends" that you use extension objects instead of IXsltContextFunction derived classes for writing your own XPath functions. Well actually it turns out that this not a recommendation - it is the only way to do it. There is no way of adding a context aware XPath extension function to the transformation. I decompiled the code that resolves functions in an XSLT (System.Xml.Xsl.XsltCompileContext.ResolveFunction) an here is what I got:

public virtual IXsltContextFunction ResolveFunction(string prefix, 
              string name, System.Xml.XPath.XPathResultType[] argTypes){
    IXsltContextFunction local0;
    string local1;
    object local2;
    MethodInfo local3;

    local0 = null;
    if (prefix == String.Empty)
        local0 = XsltCompileContext.s_FunctionTable.get_Item(name) 
                                           as IXsltContextFunction;
    else {
        local1 = this.LookupNamespace(prefix);
        if (local1 == "urn:schemas-microsoft-com:xslt" && name == "node-set")
            local0 = XsltCompileContext.s_FuncNodeSet;
        else {
            local3 = this.GetExtentionMethod(local1, name, argTypes, local2);
            if (local2 == null)
                throw new XsltException("Xslt_ScriptInvalidPrefix", prefix);
            if (local3 != null)
                local0 = new FuncExtension(local2, local3);
        }
    }
    if (local0 == null)
        throw new XsltException("Xslt_UnknownXsltFunction", name);
    if ((int) argTypes.Length < local0.Minargs || 
             local0.Maxargs < (int) argTypes.Length)
        throw new XsltException("Xslt_WrongNumberArgs", name, 
                       Convert.ToString((int) argTypes.Length));
    return local0;
}

As you can see there are 3 types of functions that the XSLT can resolve:

  • Ones that are in the root XML namespace
  • The msxsl:node-set function
  • Functions on extension objects

From the code above, it is visible that the only way of inserting a context aware function is to put it in the static hashtable s_FunctionTable field. As you have already guessed the System.Xml.Xsl.XsltCompileContext is a protected class and so is the s_FunctionTable. To access it, we will use reflection:

static Hashtable XslFunctionTable
{
    get
    {
        Assembly xmlAssembly = Assembly.LoadWithPartialName("System.Xml");
        Type tXsltCompileContext = xmlAssembly.GetType
                    ("System.Xml.Xsl.XsltCompileContext");
        FieldInfo finf = tXsltCompileContext.GetField("s_FunctionTable", 
                           BindingFlags.NonPublic | BindingFlags.Static);
        return finf.GetValue(null) as Hashtable;
    }
}

Once we have the hash table we can easily add or remove functions from it. The hashtable is initialized in the static constructor of System.Xml.Xsl.XsltCompileContext so we should avoid adding and removing functions in static constructors

Drawbacks

This is actually a hack. It works fine on .NET framework 1.0 but it may not work in future releases of the framework. I hope Microsoft fixes this problem in future. Also you can add functions only in the root XML namespace.

Possible improvements

The System.Xml assembly is located using it's partial name. This is not good because future versions of the .NET framework will use newer XML parsers and the hack wont work. The full name should be used instead.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Stefan Popov



Occupation: Web Developer
Location: Germany Germany

Other popular XML articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 7 of 7 (Total in Forum: 7) (Refresh)FirstPrevNext
General.NET 2.0 / XslCompiledTransformmemberCaledonia15:54 7 Dec '06  
GeneralRe: .NET 2.0 / XslCompiledTransformmemberStefan Popov2:02 15 Dec '06  
GeneralNo Hack NeedededitorHeath Stewart9:11 4 Mar '03  
GeneralRe: No Hack NeededmemberStefan Popov5:39 5 Mar '03  
Generallocal0 and local1 ?memberMasaaki Onishi6:22 4 Mar '03  
GeneralRe: local0 and local1 ?memberStefan Popov5:21 5 Mar '03  
GeneralWell done!memberCarel23:54 3 Mar '03  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 2 Mar 2003
Editor: Smitha Vijayan
Copyright 2003 by Stefan Popov
Everything else Copyright © CodeProject, 1999-2008
Web15 | Advertise on the Code Project