在 C# 中使用 Saxon-HE 针对 XDocument 执行具有给定上下文的 XQuery

Perform an XQuery with a given context against a XDocument with Saxon-HE in C#

在我的 C# 程序 (.NET Framework 4.8) 中,我在运行时创建了一个 XDocument。我需要在运行时 针对此 XML 树执行多个 XQuery 计算。各自的查询来自外部来源,所以我在设计时不知道它们的具体情况。查询可能包含 exists()not()empty()every $x in ... satiesfies ...generate-id().

等函数

作为第一步,我真正需要知道的是查询是否产生结果(即 returns 某事 来自 XDocument 即不为空)。

最初,我只是尝试使用 XElement.XPathEvaluate(query),只要所述查询真的只是一个 XPath 评估,它就可以正常工作 - 但如果它包含函数 - 如 exists(...) 等 -,一个错误抛出告诉我我需要一个 XsltContext。我有的是:

 public bool XPathExists(string context, string xpath)
        {     
            Object result;

            try
            {                
                XElement contextElement = xmlTree.Root.XPathSelectElement(context, namespaces);
                result = contextElement.XPathEvaluate(xpath, namespaces);
            }
            catch (Exception e) // xpath can't be evaluated
            {
                Debug.Print(e.Message);
                return false;
            }

            return (result != null);
        }

所以我想我需要使用Saxon-HE来执行查询,因为它完全支持XQuery。不幸的是,我很难使用内存中的 XDocument 作为源(或者就此使用它)正确初始化 Saxon 的 XQueryEvaluator。此外,令人沮丧的是,我不知道在哪里/如何为 Saxon 提供要在其中评估 xquery 的初始上下文节点。无论是阅读 the API documentation、Michael Kay 的书“XSLT 2.0 和 XPath 2.0”中关于将 Saxon 与 .NET 结合使用的章节,还是在互联网上进行一般搜索(尤其是 Whosebug),我都没有得到任何帮助。

到目前为止,我一直坚持这个(当然不工作)“代码”:

public bool XQueryYieldsResults(XDocument xmlTree, string contextNode, string xqueryExpression)
  {
    var processor = new Processor();
    XdmNode input = processor.NewDocumentBuilder().Build(xmlTree);

    var compiler = processor.NewXQueryCompiler();

    var exececutable = compiler.Compile(xqueryExpression); // how to set context?
    var xqueryEvaluator = exececutable.Load(); // ...?! 

    // ...
    // var result = *the xquery's result*;
    // ...

    return (result != null);
  }

很抱歉弄得一团糟,我真的不知道从何说起!关于在这里做什么的任何提示 - 或者更具体地说:如何使用 Saxon-HE 对 XDocument 执行具有给定上下文的 XQuery - 将不胜感激! :-)

XDocument 和 Saxon 的 DocumentBuilder 之间唯一合适的接口是采用 XmlReaderBuild 方法:https://www.saxonica.com/html/documentation/dotnetdoc/Saxon/Api/DocumentBuilder.html#Build(XmlReader).

所以 XdmNode input = processor.NewDocumentBuilder().Build(xmlTree.CreateReader()) 应该适用于 运行 XPath 3.1 或 XQuery 3.1。但是,您将无法将结果追溯到 XDocument 中的节点。

var processor = new Processor();
XdmNode input = processor.NewDocumentBuilder().Build(xmlTree.CreateReader());

var compiler = processor.NewXQueryCompiler();

var exececutable = compiler.Compile(xqueryExpression); 
var xqueryEvaluator = exececutable.Load();

xqueryEvaluator.ContextItem = input;

XdmItem result = xqueryEvaluator.EvaluateSingle();

return (result != null);

另一方面,大多数带有 notemptyevery .. 的示例将始终 return 布尔值而不是 null。

我不太明白 contextNode 作为字符串在您的方法中有什么价值。所以上面应该 运行 任何针对完整文档的 XQuery。

如果您只是想 运行 XQuery 或 XPath 使用表达式检查布尔值,那么这里是一个示例:

            string[] examples = { "exists(//foo)", "not(//bar)", "empty(//bar)", @"every $x in //item satisfies matches($x/foo, '^\p{L}+$')" };

            XDocument doc = XDocument.Parse(@"<root>
  <items>
    <item>
      <foo>a</foo>
    </item>
    <item>
      <foo>b</foo>
    </item>
  </items>
</root>");

            Processor processor = new Processor();

            XPathCompiler xpathCompiler = processor.NewXPathCompiler();

            DocumentBuilder docBuilder = processor.NewDocumentBuilder();

            XdmNode xdmDoc = docBuilder.Build(doc.CreateReader());

            foreach (string expression in examples)
            {
                Console.WriteLine("{0} evaluates to {1}.", expression, xpathCompiler.EvaluateSingle(expression, xdmDoc));
            }

XQueryCompilerXQueryEvaluator如下:

            string[] examples = { "exists(//foo)", "not(//bar)", "empty(//bar)", @"every $x in //item satisfies matches($x/foo, '^\p{L}+$')" };

            XDocument doc = XDocument.Parse(@"<root>
  <items>
    <item>
      <foo>a</foo>
    </item>
    <item>
      <foo>b</foo>
    </item>
  </items>
</root>");

            Processor processor = new Processor();
    
            XQueryCompiler xqueryCompiler = processor.NewXQueryCompiler();

            DocumentBuilder docBuilder = processor.NewDocumentBuilder();

            XdmNode xdmDoc = docBuilder.Build(doc.CreateReader());

            foreach (string expression in examples)
            {
                XQueryEvaluator xqueryEvaluator = xqueryCompiler.Compile(expression).Load();
                xqueryEvaluator.ContextItem = xdmDoc;
                Console.WriteLine("Expression {0} evaluates to {1}.", expression, xqueryEvaluator.EvaluateSingle());
            }

来自您的 XDocument xmlTree 和 link Martin 提供的。

            string xml = xmlTree.ToString();
            StringReader sReader = new StringReader(xml);
            XmlReader xReader = XmlReader.Create(sReader);
            XdmNode node = Build(reader);