【问题标题】:Perform an XQuery with a given context against a XDocument with Saxon-HE in C#在 C# 中使用 Saxon-HE 对 XDocument 执行具有给定上下文的 XQuery
【发布时间】:2021-01-18 13:11:58
【问题描述】:

在我的 C# 程序 (.NET Framework 4.8) 中,我在运行时创建了一个 XDocument。我需要针对这个 XML 树在运行时执行几个 XQuery 评估。相应的查询来自外部来源,所以我在设计时不知道它们的细节。查询可能包含 exists()not()empty()every $x in ... satiesfies ...generate-id() 等函数。

作为第一步,我真正需要知道的是查询是否产生结果(即从 XDocument 返回的 something 不为空)。

最初,我只是尝试使用 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。不幸的是,我很难正确初始化 Saxon 的 XQueryEvaluator,并使用内存中的 XDocument 作为源(或者根本就使用它)。此外,令人沮丧的是,我不知道在哪里/如何向 Saxon 提供要在其中评估 xquery 的初始上下文节点。无论是阅读the API documentation,还是 Michael Kay 的书“XSLT 2.0 和 XPath 2.0”中关于将 Saxon 与 .NET 结合使用的章节,也没有一般地搜索互联网(尤其是 StackOverflow),都没有让我有所收获。

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

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 - 将不胜感激! :-)

【问题讨论】:

  • @MartinHonnen 和@jdweng 正确地指出,Saxon(当前)没有能力直接针对 XML 的 XDocument 树模型运行;使用它的唯一方法是将其重建为 DOM 文档 (XmlDocument) 或 Saxon TinyTree。
  • 感谢您指出这一点。

标签: c# xml linq-to-xml xquery saxon


【解决方案1】:

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 .. 的大多数示例将始终返回布尔值而不是 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());
            }

【讨论】:

  • 感谢您的详细解答!我现在意识到,为了简化和澄清问题,我可能问错了问题。由于您的回答为发布的问题提供了解决方案,因此我将其标记为解决方案并将其保留在此处,以便其他有此实际问题的人可以找到它。尽管如此,我还是创建了一个具有更多上下文的新问题,希望它更清楚:stackoverflow.com/questions/64216807/…
【解决方案2】:

来自您的 XDocument xmlTree 和 Martin 提供的链接。

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

【讨论】:

    猜你喜欢
    • 2019-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多