在 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 之间唯一合适的接口是采用 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 ..
的示例将始终 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));
}
同XQueryCompiler
和XQueryEvaluator
如下:
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);
在我的 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 之间唯一合适的接口是采用 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 ..
的示例将始终 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));
}
同XQueryCompiler
和XQueryEvaluator
如下:
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);