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

问题描述

在C#程序(.NET Framework 4.8)中,我在运行时创建了XDocument。我需要针对此XML树在运行时执行几个XQuery评估。各个查询来自外部资源,因此在设计时我不知道它们的详细信息。查询可能包含诸如exists()not()empty()之类的函数或诸如every $x in ... satiesfies ...generate-id()之类的东西。

第一步,我真正需要知道的是查询是否产生结果(即从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结合使用的一章,也没有普遍地搜索互联网(特别是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 -非常感谢! :-)

解决方法

XDocument和Saxon的DocumentBuilder之间唯一合适的接口是const handleUserChange = useCallback((user) => { setCurrentUser(user); setPending(false); },[]); useEffect(() => { if (pending) { AccountHandler.getInstance().registerAuthStateChangeObserver(handleUserChange) }; return () => { // here you need to unsubscribe AccountHandler.getInstance().unregisterAuthStateChangeObserver(handleUserChange); } },[])方法,该方法采用Buildhttps://www.saxonica.com/html/documentation/dotnetdoc/Saxon/Api/DocumentBuilder.html#Build(XmlReader)

因此XmlReader应该可以运行XPath 3.1或XQuery 3.1。但是,您将无法将结果追溯到XDocument中的节点。

XdmNode input = processor.NewDocumentBuilder().Build(xmlTree.CreateReader())

另一方面,您的大多数带有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); notempty的示例将始终返回布尔值,而不是null。

我不太了解every ..作为字符串在您的方法中具有什么值。因此,上面的代码应该针对完整的文档运行任何XQuery。

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

contextNode

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
,

从您的XDocument xmlTree并提供Martin提供的链接。

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