2
votes

Hi I try to implement an extension for saxon in C#. I use the saxon9he interface. The extension itself works fine but now I want to use XPath expressions to get values from a node. I break it down to the relevant code part (the rest is working fine).

The extension has two arguments. The first one is a string, the second one is a node-set.

public override IXdmEnumerator Call(IXdmEnumerator[] arguments, DynamicContext context)
{
    if (arguments.Length == 2)
    {
        arguments[0].MoveNext();

        string text = (arguments[0].Current as XdmAtomicValue).Value as string;
        IXdmEnumerator enumerator = arguments[1];

        while (enumerator.MoveNext())
        {
            XdmNode node = (XdmNode)enumerator.Current;

            // how can I get values from node here by using XPath expressions?
            // e.g. I want the value of the attribute "type" of the subnode "xy"
            // XPath would be something like this: "./xy/@type"

            text = text.Replace(node.NodeName.LocalName, node.StringValue);
        }

        var result = new XdmAtomicValue(text);

        return (IXdmEnumerator)result.GetEnumerator();
    }
    ...
}

The 3 comments in the middle show my problem. I want to access subnodes, attributes and so on by XPath expressions. This is a simplified version. The XPath should be passed as an additional argument later. So it is not a fixed XPath expression that I could transform to code. I really need an XPath evaluator.

I saw a solution by creating an XPathEvaluator from a Processor. But I have no Processor at this point, or do I?

Thanks for help.

Here is the solution (thanks to Michael):

var configuration = context.Implementation.getConfiguration();
var processor = (Processor)configuration.getProcessor();
var xpathCompiler = processor.NewXPathCompiler();

while (enumerator.MoveNext())
{
    XdmNode node = (XdmNode)enumerator.Current;

    var keyResult = xpathCompiler.Evaluate(searchXPath, node);
    var valueResult = xpathCompiler.Evaluate(replaceXPath, node);

    string key = "";
    string value = "";

    if (keyResult is XdmAtomicValue)
        key = (string)(keyResult as XdmAtomicValue).Value;
    else if (keyResult is XdmNode)
        key = (string)(keyResult as XdmNode).StringValue;

    if (valueResult is XdmAtomicValue)
        value = (string)(valueResult as XdmAtomicValue).Value;
    else if (valueResult is XdmNode)
        value = (string)(valueResult as XdmNode).StringValue;

    if (string.IsNullOrWhiteSpace(key) || value == null)
        continue;

    text = text.Replace(key, value);
}

Solution for Saxon 9.7:

The solution above doesn't work for Saxon 9.7 anymore. In this case I pass the processor to the extension classes and from there to the extension call classes at the time I register the extensions.

public static void RegisterSaxonExtensions(Saxon.Api.Processor processor)
{
   processor.RegisterExtensionFunction(new MyExtension1(processor));
   processor.RegisterExtensionFunction(new MyExtension2(processor));
}

...

public class MyExtension1 : Saxon.Api.ExtensionFunctionDefinition
{
     private Saxon.Api.Processor processor = null;

     public MyExtension1(Saxon.Api.Processor processor)
     {
         this.processor = processor;
     }

     public override ExtensionFunctionCall MakeFunctionCall()
     {
         return new MyExtension1Call(this.processor);
     }

     ...
}

public class MyExtension1Call : Saxon.Api.ExtensionFunctionCall
{
     private Saxon.Api.Processor processor = null;

     public MyExtension1Call(Saxon.Api.Processor processor)
     {
         this.processor = processor;
     }

     public override IXdmEnumerator Call(IXdmEnumerator[] arguments, DynamicContext context)
     {
         if (arguments.Length == 2)
         {
             arguments[0].MoveNext();

             string text = (arguments[0].Current as XdmAtomicValue).Value as string;
             IXdmEnumerator enumerator = arguments[1];
             var xpathCompiler = this.processor.NewXPathCompiler();

             while (enumerator.MoveNext())
             {
                 XdmNode node = (XdmNode)enumerator.Current;

                 var keyResult = xpathCompiler.Evaluate(searchXPath, node);
                 var valueResult = xpathCompiler.Evaluate(replaceXPath, node);

                 string key = "";
                 string value = "";

                 if (keyResult is XdmAtomicValue)
                     key = (string)(keyResult as XdmAtomicValue).Value;
                 else if (keyResult is XdmNode)
                     key = (string)(keyResult as XdmNode).StringValue;

                 if (valueResult is XdmAtomicValue)
                     value = (string)(valueResult as XdmAtomicValue).Value;
                 else if (valueResult is XdmNode)
                     value = (string)(valueResult as XdmNode).StringValue;

                 if (string.IsNullOrWhiteSpace(key) || value == null)
                     continue;

                 text = text.Replace(key, value);
             }

             var result = new XdmAtomicValue(text);

             return (IXdmEnumerator)result.GetEnumerator();
         }
     }
}
1

1 Answers

0
votes

DynamicContext.Implementation gives you an XPathContext object, this has a getConfiguration() method to get to the Configuration, and the Processor object should be found in Configuration.getProcessor(). From that you should be able to create an XPathEvaluator.