Saxon 扩展:XdmNode 上的 XPath

Posted

技术标签:

【中文标题】Saxon 扩展:XdmNode 上的 XPath【英文标题】:Saxon extension: XPath on XdmNode 【发布时间】:2017-06-28 14:41:10 【问题描述】:

您好,我尝试在 C# 中实现 saxon 的扩展。我使用 saxon9he 界面。扩展本身工作正常,但现在我想使用 XPath 表达式从节点获取值。我将其分解为相关的代码部分(其余部分工作正常)。

扩展有两个参数。第一个是字符串,第二个是节点集。

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();
    
    ...

中间的 3 个 cmets 显示了我的问题。我想通过 XPath 表达式访问子节点、属性等。这是一个简化版本。 XPath 应该稍后作为附加参数传递。所以它不是我可以转换为代码的固定 XPath 表达式。我真的需要一个 XPath 求值器。

我看到了通过从处理器创建 XPathEvaluator 的解决方案。但是我现在没有处理器,或者我没有?

感谢您的帮助。

这是解决方案(感谢 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);

Saxon 9.7 的解决方案:

上述解决方案不再适用于 Saxon 9.7。在这种情况下,我在注册扩展时将处理器传递给扩展类并从那里传递给扩展调用类。

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】:

DynamicContext.Implementation 给你一个 XPathContext 对象,它有一个 getConfiguration() 方法来获取配置,处理器对象应该在 Configuration.getProcessor() 中找到。从中您应该能够创建一个 XPathEvaluator。

【讨论】:

嗨迈克尔。我从 Saxon HE 9.5 切换到 HE 9.7。现在我的代码失败了,因为configuration.getProcessor() 现在返回net.sf.saxon.s9api.Processor 而不是Saxon.Api.Processor。我想界面是相似的,但我可以以某种方式使用 Saxon.Api 命名空间中的类吗?或者我应该调整代码以使用来自net.sf.saxon.s9api 命名空间的代码? 如果是后者:如何将Saxon.Api.XdmNode 转换为net.sf.saxon.s9api.XdmItemnet.sf.saxon.s9api.XPathCompiler.evaluate 方法需要作为输入?以及如何将evaluate 方法(net.sf.saxon.s9api.XdmValue)的结果转换回Saxon.Api.XdmValue 我怀疑这是在 9.7 中我们将 .NET 上的 Saxon.Api 接口重写为 net.sf.saxon.s9api Java 类之上的一个薄层这一事实的副作用。在 .NET 上,您不应该直接在应用程序中使用 s9api 接口。但是这里没有足够的信息来了解到底发生了什么。 问题是DynamicContext.Implementation 在 9.7 中是 net.sf.saxon.expr.XPathContext,而在 9.5 中不再有 Saxon.Api.XPathContext。那么如何在 9.7 中从扩展的 Call 方法中访问 Saxon.Api.Processor 呢? 好的,在创建/注册扩展时,我通过将处理器传递给扩展调用类来让它工作。

以上是关于Saxon 扩展:XdmNode 上的 XPath的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Node 中使用 saxon-js 处理 XPath 表达式

Saxon-HE 9.3 的 javax.xml.xpath.XPathFactory 提供程序配置文件中的语法错误

如果 Saxon 在 CLASSPATH 上,则无法识别命名空间的 XPath 表达式失败

将 Xalan 与 Saxon 一起使用

在 node.js 中评估 xpath2.0

遍历 Saxon-JS 序列