在 System.Xml.XPath 中启用 XPath2 查询(XPathException: invalid token) [英] Enable XPath2 queries in System.Xml.XPath (XPathException: invalid token)

查看:28
本文介绍了在 System.Xml.XPath 中启用 XPath2 查询(XPathException: invalid token)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Microsoft 的 System.Xml.XPath nuget-package 可用于 .NET 4.6,声称支持 XPath 1.0 和 2.0.De 文档 说描述命名空间:

Microsofts System.Xml.XPath nuget-package, available for .NET 4.6, claims to support XPath 1.0 and 2.0. De documentation says descibes the namespace:

System.Xml.XPath 命名空间包含定义用于导航和编辑 XML 信息项作为 XQuery 1.0 和 XPath 2.0 数据模型实例的游标模型的类.

在升级 Visual Studio、升级和我所有的项目到框架版本 4.6 之后,我仍然无法让最简单的 XPath-2.0 for-expression 工作.根据规范,它们应该可以工作.

After upgrading Visual Studio, upgrading and all my projects to framework version 4.6 I still can't get the simplest XPath-2.0 for-expression to work. According to the specification, they should work.

我无法想象微软声称支持它实际上不支持的东西,所以很明显我做错了.如何正确使用 XPath2 查询?

I can't imagine Microsoft claiming to support something it actually doesn't, so obviously I'm doing something wrong. How do I correctly use an XPath2 query?

[TestMethod]
public void TestXPath2()
{
    // The System.Xml.XPath namespace contains the classes that define a cursor model for navigating and editing XML information items as instances of the 
    // XQuery 1.0 and XPath 2.0 Data Model.

    var expression = "for $x in /Root/Foo/Bar return $x";
    var compiledExpression = System.Xml.XPath.XPathExpression.Compile(expression); 
    // throws XPathException: "for ... has an invalid token"
}

附言我真正想要的是让这样的东西工作:

P.S. What I actually want, is to get something like this to work:

    [TestMethod]
    public void TestLibraryForCustomer1()
    {
        string xmlFromMessage = @"<Library>
            <Writer ID=""writer1""><Name>Shakespeare</Name></Writer>
            <Writer ID=""writer2""><Name>Tolkien</Name></Writer>
            <Book><WriterRef REFID=""writer1"" /><Title>King Lear</Title></Book>
            <Book><WriterRef REFID=""writer2"" /><Title>The Hobbit</Title></Book>
            <Book><WriterRef REFID=""writer2"" /><Title>Lord of the Rings</Title></Book>
             </Library>"; 

        var titleXPathFromConfigurationFile = "./Title"; 
        var writerXPathFromConfigurationFile = "for $curr in . return /Library/Writer[@ID=$curr/WriterRef/@REFID]/Name";

        var library = ExtractBooks(xmlFromMessage, titleXPathFromConfigurationFile, writerXPathFromConfigurationFile).ToDictionary(b => b.Key, b => b.Value);

        Assert.AreEqual("Shakespeare", library["King Lear"]);
        Assert.AreEqual("Tolkien", library["The Hobbit"]);
        Assert.AreEqual("Tolkien", library["Lord of the Rings"]);
    }

    [TestMethod]
    public void TestLibraryForCustomer2()
    {
        string xmlFromMessage = @"<Library>
                <Writer ID=""writer1"">
                    <Name>Shakespeare</Name>
                    <Book><Title>Sonnet 18</Title></Book>
                </Writer>
                <Writer ID=""writer2"">
                    <Name>Tolkien</Name>
                    <Book><Title>The Hobbit</Title></Book>
                    <Book><Title>Lord of the Rings</Title></Book>
                </Writer>
            </Library>";

        var titleXPathFromConfigurationFile = "./Title";
        var writerXPathFromConfigurationFile = "../Name";

        var library = ExtractBooks(xmlFromMessage, titleXPathFromConfigurationFile, writerXPathFromConfigurationFile).ToDictionary(b => b.Key, b => b.Value);

        Assert.AreEqual("Shakespeare", library["Sonnet 18"]);
        Assert.AreEqual("Tolkien", library["The Hobbit"]);
        Assert.AreEqual("Tolkien", library["Lord of the Rings"]);
    }


    public IEnumerable<KeyValuePair<string,string>> ExtractBooks(string xml, string titleXPath,  string writerXPath)
    {
        var library = XDocument.Parse(xml);
        foreach(var book in library.Descendants().Where(d => d.Name == "Book"))
        {
            var title = book.XPathSelectElement(titleXPath).Value;
            var writer = book.XPathSelectElement(writerXPath).Value;
            yield return new KeyValuePair<string, string>(title, writer);
        }
    }

推荐答案

Tomalek 指出正确:

Tomalek pointed out correctly:

  • .NET(在 System.Xml.XPath 中)不支持 XPath 2.0,期间
  • 数据模型和查询语言是分开的.

所以我使用第三方 XPath 2 库解决了这个问题,XPath2 nuget 包.这允许像

So I solved it by using a third party XPath 2 library, The XPath2 nuget package. This allows for expressions like

for $c in . return ../Writer[@ID=$c/WriterRef/@REFID]/Name

请注意,我需要使用从书到作者的相对路径.这不起作用:

Note that I needed to use a relative path from book to writer. This does not work:

# does not work due to the absolute path
for $c in . return /Library/Writer[@ID=$c/WriterRef/@REFID]/Name

供将来参考:此代码在安装 nuget 包后有效:

For future reference: this code works after installing the nuget pacage:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Wmhelp.XPath2;

namespace My.Library
{
    [TestClass]
    public class WmhelpTests
    {
        [TestMethod]
        public void LibraryTest()
        {
            string xmlFromMessage = @"<Library>
                <Writer ID=""writer1""><Name>Shakespeare</Name></Writer>
                <Writer ID=""writer2""><Name>Tolkien</Name></Writer>
                <Book><WriterRef REFID=""writer1"" /><Title>King Lear</Title></Book>
                <Book><WriterRef REFID=""writer2"" /><Title>The Hobbit</Title></Book>
                <Book><WriterRef REFID=""writer2"" /><Title>Lord of the Rings</Title></Book>
            </Library>";

            var titleXPathFromConfigurationFile = "./Title";
            var writerXPathFromConfigurationFile = "for $curr in . return ../Writer[@ID=$curr/WriterRef/@REFID]/Name";

            var library = ExtractBooks(xmlFromMessage, titleXPathFromConfigurationFile, writerXPathFromConfigurationFile).ToDictionary(b => b.Key, b => b.Value);

            Assert.AreEqual("Shakespeare", library["King Lear"]);
            Assert.AreEqual("Tolkien", library["The Hobbit"]);
            Assert.AreEqual("Tolkien", library["Lord of the Rings"]);
        }


        [TestMethod]
        public void TestLibraryForCustomer2()
        {
            string xmlFromMessage = @"<Library>
                <Writer ID=""writer1"">
                    <Name>Shakespeare</Name>
                    <Book><Title>Sonnet 18</Title></Book>
                </Writer>
                <Writer ID=""writer2"">
                    <Name>Tolkien</Name>
                    <Book><Title>The Hobbit</Title></Book>
                    <Book><Title>Lord of the Rings</Title></Book>
                </Writer>
            </Library>";

            var titleXPathFromConfigurationFile = "./Title";
            var writerXPathFromConfigurationFile = "../Name";

            var library = ExtractBooks(xmlFromMessage, titleXPathFromConfigurationFile, writerXPathFromConfigurationFile).ToDictionary(b => b.Key, b => b.Value);

            Assert.AreEqual("Shakespeare", library["Sonnet 18"]);
            Assert.AreEqual("Tolkien", library["The Hobbit"]);
            Assert.AreEqual("Tolkien", library["Lord of the Rings"]);
        }


        public IEnumerable<KeyValuePair<string, string>> ExtractBooks(string xml, string titleXPath, string writerXPath)
        {
            var library = XDocument.Parse(xml);
            foreach (var book in library.Descendants().Where(d => d.Name == "Book"))
            {
                var title = book.XPath2SelectElement(titleXPath).Value;
                var writer = book.XPath2SelectElement(writerXPath).Value;
                yield return new KeyValuePair<string, string>(title, writer);
            }
        }
    }
}

这篇关于在 System.Xml.XPath 中启用 XPath2 查询(XPathException: invalid token)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆