Native Modules

A guide to using modules in XmlPrime, and writing Modules as .NET types.

This topic contains the following sections.

Overview

XmlPrime allows modules to be implemented in .NET languages. There are several mechanisms for doing this. Native modules can be created programmatically from a .NET class. They can also be constructed directly within an XSLT stylesheet by using the <xp:script><xp:script><xp:script> or <msxsl:script><msxsl:script><msxsl:script> elements. They can also be created either from source files or annotated DLLs imported in an XQuery program.

Programmatic creation of native modules

A module implemented by a .NET class contains a number of functions which are implemented as methods on the class. Which functions are exported, and the signatures of these functions can be controlled with the use of attributes.

A class is converted to an XdmModuleXdmModuleXdmModule instance by calling the XdmModule.NativeModule<T> ()XdmModule.NativeModule(Of T) ()XdmModule::NativeModule<T> () method passing the class as a template parameter. This module can then be added to the library set for use in an XQuery program or XPath expression. A class used in this way is called a native module. Note that more than one module can implement a library with a particular namespace. A library can for example consist of an XQuery module and a native module.

If the target namespace of a module is not otherwise specified then it defaults to the URI with scheme clitype and path equal to the CLI full name of the .NET type. For example the class System.MathSystem.MathSystem::Math would be exported as a module with target namespace clitype:System.Math.

Restrictions for native functions

There are a number of restrictions on the functions that can be made available to an XQuery program or XPath expression. Many of these restrictions may be lifted in future versions.

  • If a function is implemented by a non-static method, then the class must have a public default constructor. In a future version of XmlPrime we hope to allow an instantiation of a class to be specified in the dynamic context settings.
  • Methods cannot take any external state. In a future version we hope to allow extra state information to be passed through to native functions called from an XQuery program or XPath expression.
  • Methods called from an XQuery program or XPath expression must be pure (have no side effects). Examples of side effects are writing a message to the console, or saving a result to a file. This is because there is no guarantee of what order user defined functions will be called in, how many times they will be called, or whether they will even be called, and these things will all be affected by query optimization. In a future version of XmlPrime we hope to support impure functions (via XQuery Scripting Extensions).
  • Methods called from an XQuery program or XPath expression must be deterministic. That is, when the function is called with the same arguments, it must return the same value. Examples of functions that are not deterministic are a function that returns a random number, or one that reads the contents of the specified file (as the contents may change during evaluation). This is required as optimizations such as common subexpression elimination assume this property. In a future version of XmlPrime we hope to support non-deterministic functions as well.

Native type mappings

The parameter and return types of methods exposed to XmlPrime are currently restricted to those that can be converted to XQuery types. Items can be represented by any of the CLR types listed in Mapping Atomic Types to CLR Types.

Sequences of items can be specified as arguments of the following types. The generic parameter must be set to one of the item types specified above.

T (value type) A sequence of exactly one item.
T (reference type) A sequence of zero or one item.
T? (value type) A sequence of zero or one item.
T[] Evaluated eagerly (entire sequence is evaluated together).
List<T>List(Of T)List<T> Evaluated eagerly (entire sequence is evaluated together).
IEnumerable<T>IEnumerable(Of T)IEnumerable<T> Evaluated lazily (items are evaluated as they are retrieved).
IEnumerator<T>IEnumerator(Of T)IEnumerator<T> Evaluated lazily (items are evaluated as they are retrieved).

Module annotations

Native libraries can be annotated via the use of XdmModuleAttributeXdmModuleAttributeXdmModuleAttribute, XdmFunctionAttributeXdmFunctionAttributeXdmFunctionAttribute and XdmTypeAttributeXdmTypeAttributeXdmTypeAttribute.

Annotating a class with the XdmModuleAttributeXdmModuleAttributeXdmModuleAttribute attribute adds greater control over the functions that are exported, and the names used to export them. The XdmModuleAttributeXdmModuleAttributeXdmModuleAttribute also allows the target namespace of the module to be specified.

XdmFunctionAttribute

Annotating a method with the XdmFunctionAttributeXdmFunctionAttributeXdmFunctionAttribute indicates that this method should be exported as part of the module. The LocalNameLocalNameLocalName property specifies the local name of the exported function. If the local name is not set, then the name of the method is used. If the class is annotated with the XdmModuleAttributeXdmModuleAttributeXdmModuleAttribute, and a containing is not annotated with the XdmFunctionAttributeXdmFunctionAttributeXdmFunctionAttribute then it will not be exported as part of the module.

XdmTypeAttribute

The XdmTypeAttributeXdmTypeAttributeXdmTypeAttribute can be used to specify the declared type of an argument or of the return type of the function. The attribute contains two properties, TypeCodeTypeCodeTypeCode and QuantifierQuantifierQuantifier; both of which must be set, and together which define the type. The TypeCodeTypeCodeTypeCode property is used to define the types of items in the sequence type, and the QuantifierQuantifierQuantifier property is used to define its cardinality.

Some .NET types can be used to represent more than one XQuery type. For example, xs:untypedAtomic is represented by stringStringString, so to write a function that returns xs:untypedAtomic values, you can write a function that returns a stringStringString and mark it with the following attribute:

 
[XdmType(XmlTypeCode.UntypedAtomic,Quantifier.One)]
 

Example

The following code demonstrates a module implementing a factorial function:

 
[XdmModule("http://www.example.org")]
class MyModule
{
    [XdmFunction("factorial")]
    public XPathAtomicValue MyFactorialFunction([XdmType(XmlTypeCode.Integer)] int argument)
    {
        if (argument <= 0)
            return 0;
        
        int factorial = 1;
        for (int i = 1; i <= argument; i++)
            factorial *= i;

        return factorial
    }
}
 

Note that this class is annotated to export the function factorial in the namespace http://www.example.org. The following code shows how to compile a query that uses this external module to calculate 10 factorial, and write out the result to the console.

 
XQuerySettings querySettings = new XQuerySettings();
XdmModule module = XdmModule.NativeModule<MyModule>();
querySettings.Libraries.Add(module);

string program = "import module namespace module='http://www.example.org';" + Environment.NewLine +
                 "module:factorial(10)";

XQuery query = XQuery.Compile(program, querySettings);

XPathItem result = query.EvaluateToItem();

Console.WriteLine(result.ToString());
 

For more information on creating a native module, see the documentation for the XdmModule.NativeModule<T> ()XdmModule.NativeModule(Of T) ()XdmModule::NativeModule<T> () method.

Creating and accessing modules from XSLT

Native modules can be constructed directly within an XSLT stylesheet by using the <xp:script><xp:script><xp:script> or <msxsl:script><msxsl:script><msxsl:script> elements, the latter being available for compatibility with the Microsoft extension element available in XslCompiledTransformXslCompiledTransformXslCompiledTransform. For details see Query and XSLT Language Extensions.

The following example uses the <msxsl:script><msxsl:script><msxsl:script> element.

 
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
								version="1.0"
								xmlns:msxsl="urn:schemas-microsoft-com:xslt"
								exclude-result-prefixes="msxsl xsl">

	<xsl:output method="xml" encoding="UTF-8" indent="no"/>

	<msxsl:script implements-prefix="foo" language="JScript">
		function three() {
			return 3
		}
	</msxsl:script>

	<xsl:template match="/">
    <xsl:value-of select="foo:three()" />
	</xsl:template>

</xsl:stylesheet>
 

The following example uses the <xp:script><xp:script><xp:script> element.

 
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
		version="2.0"
		xmlns:xp="http://www.xmlprime.com/"
		xmlns:xs="http://www.w3.org/2001/XMLSchema"
		xmlns:foo="urn:foo"
		exclude-result-prefixes="xp foo xs">

  <xsl:output method="xml" encoding="UTF-8" indent="no"/>

  <xp:script implements-prefix="foo" language="C#">
  ![CDATA[
    public static IEnumerable<XPathItem> Two(XPathItem x) { return new XPathItem[] {x, x}; }
    ]]<
  </xp:script>

  <xsl:template match="/">
    <foo>
      <xsl:sequence select="foo:Two(1)" />
    </foo>
  </xsl:template>

</xsl:stylesheet>

 

Creating and accessing modules from XQuery

A module implemented by a .NET class contains a number of functions which are implemented as methods on the class. Which functions are exported, and the signatures of these functions can be controlled with the use of attributes.

Currently, scripts may be imported from a DLL (with file extension ".dll"), C# (with file extension ".cs") or from Visual Basic (with file extension ".vb").

The following example demonstrates import from a C# script in file "script.cs".

 
[XdmType(System.Xml.Schema.XmlTypeCode.Item)]
public static XPathItem IdentityItem([XdmType(System.Xml.Schema.XmlTypeCode.Item)] XPathItem x) { return x; }
 
 
import module namespace fx = "http://www.xmlprime.com/xpath" at "script.cs";

fx:IdentityItem(1);