在 C# 中使用 Saxon-HE 对 XDocument 执行具有给定上下文的 XQuery
Posted
技术标签:
【中文标题】在 C# 中使用 Saxon-HE 对 XDocument 执行具有给定上下文的 XQuery【英文标题】:Perform an XQuery with a given context against a XDocument with Saxon-HE in C# 【发布时间】: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 结合使用的章节,也没有一般地搜索互联网(尤其是 ***),都没有让我有所收获。
到目前为止,我一直坚持使用这个(当然是不工作的)“代码”:
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。
感谢您指出这一点。
【参考方案1】:
XDocument 和 Saxon 的 DocumentBuilder 之间唯一合适的接口是采用 XmlReader
的 Build
方法: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);
另一方面,not
、empty
、every ..
的大多数示例将始终返回布尔值而不是 null。
我还不太明白contextNode
作为字符串在您的方法中具有什么价值。所以上面应该对完整的文档运行任何 XQuery。
如果您只是想用表达式检查布尔值来运行 XQuery 或 XPath,那么下面是一个示例:
string[] examples = "exists(//foo)", "not(//bar)", "empty(//bar)", @"every $x in //item satisfies matches($x/foo, '^\pL+$')" ;
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));
XQueryCompiler
和XQueryEvaluator
同理如下:
string[] examples = "exists(//foo)", "not(//bar)", "empty(//bar)", @"every $x in //item satisfies matches($x/foo, '^\pL+$')" ;
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());
【讨论】:
感谢您的详细解答!我现在意识到,为了简化和澄清问题,我可能问错了问题。由于您的回答为发布的问题提供了解决方案,因此我将其标记为解决方案并将其保留在此处,以便其他有此实际问题的人可以找到它。尽管如此,我还是创建了一个具有更多上下文的新问题,希望它更清楚:***.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);
【讨论】:
以上是关于在 C# 中使用 Saxon-HE 对 XDocument 执行具有给定上下文的 XQuery的主要内容,如果未能解决你的问题,请参考以下文章