如何在使用XElement或LINQ时使用XPath?

83
考虑以下 XML:

Consider the following XML:

<response>
  <status_code>200</status_code>
  <status_txt>OK</status_txt>
  <data>
    <url>http://bit.ly/b47LVi</url>
    <hash>b47LVi</hash>
    <global_hash>9EJa3m</global_hash>
    <long_url>http://www.tumblr.com/docs/en/api#api_write</long_url>
    <new_hash>0</new_hash>
  </data>
</response>

我正在寻找一种非常简短的方法来获取<hash>元素的值。 我尝试过:

var hash = xml.Element("hash").Value;

但是那样并不起作用。是否可能向XElement提供XPath查询?使用旧的System.Xml框架,我可以这样做:

xml.Node("/response/data/hash").Value

在LINQ命名空间中有类似这样的东西吗?


更新:

在更多的尝试后,我找到了一种方法来实现我想要做的事情:

var hash = xml.Descendants("hash").FirstOrDefault().Value;

我仍然很想看看是否有更好的解决方案?


3
在这种情况下不要使用FirstOfDefault(),因为如果未找到"hash",你将会得到一个NullReferenceException异常。使用First()代替,你将得到一个更详细的异常。 - kaalus
3
如果你期望"hash"总是存在,就使用First()。否则,只要在访问Value属性之前检查是否为null,FirstOrDefault()就可以了。 - an phu
6个回答

148

要使用LINQ to XML的XPath功能,需要在代码中添加System.Xml.XPath的引用声明。这样可以将System.Xml.XPath.Extensions扩展方法引入作用域。

在您的示例中:

var value = (string)xml.XPathEvaluate("/response/data/hash");

1
好的,看起来最接近原始问题。 - H H
3
实际上,它现在位于 System.Xml.XPath 中。 - Dan Friedman
@DanFriedman 它还没有移动。请注意,链接是到类文档,稍早时我提供了命名空间(没有链接)。 - Richard
还要注意,你需要通过NuGet为UWP应用程序添加一个程序集:链接 - Matt__C
1
我发现使用XPathSelectElement方法并返回XElement而不是对象更容易。 - user3042674

40

其他人已经合理地建议如何使用“本机”的LINQ to XML查询来完成您想要的操作。

然而,为了提供更多选择,在考虑使用XPathSelectElementXPathSelectElementsXPathEvaluate来针对一个XNode计算XPath表达式(它们都是XNode扩展方法)。你也可以使用CreateNavigator来创建一个XPathNavigator用于一个XNode

就我个人而言,作为一个大的LINQ粉丝,我非常喜欢直接使用LINQ to XML API,但如果您更喜欢XPath,上述内容可能对您有所帮助。


14

当使用LINQ to XML时,为什么不使用LINQ来获取实际对象。

Descendants方法会从整个XML中找到每个元素,并列出与指定名称匹配的所有对象。因此,在您的情况下,它找到了名称为“hash”的对象。

所以,与其这样做:

var hash = xml.Descendants("hash").FirstOrDefault().Value;

我会像这样分崩离析:

var elements = xml.Descendants("hash");
var hash = elements.FirstOrDefault();

if(hash != null)
 hash.Value // as hash can be null when default. 

通过这种方式,您还可以获取属性、节点元素等。

查看这篇文章,以便更清楚地了解它,从而有所帮助。 http://www.codeproject.com/KB/linq/LINQtoXML.aspx 我希望这能帮到您。


2
@adhishek,感谢您解释将元素分离到自己的变量中以便进行其他操作(如获取属性等)的价值。+1 - Paul Fryer
1
使用XPath的一个原因是您会失去Linq无法做到的所有这些事情。首先显而易见的一个是执行在运行时定义的查询(例如从配置文件或其他地方读取的表达式)。 - Marco Mp

9
您可以使用.Element()方法将元素链接起来形成类似XPath的结构。
以您的示例为例:
XElement xml = XElement.Parse(@"...your xml...");
XElement hash = xml.Element("data").Element("hash");

1
我认为这是最好的答案,因为它在继续使用 LINQ to XML(推荐)而不是使用 XPath with LINQ to XML 查询(不推荐)的情况下完成了工作。 - Girish Jain
2
使用XPath更加简洁,特别是当你正在寻找任何子孙节点或更远的节点时。 - an phu
Element() 可能返回 null,因此这是不安全的。 - Simon Morgan

3

我尝试设计了一个类似于LINQ的框架来生成XPath。它允许使用C# Lambda表达式来描述XPath。

var xpath = CreateXpath.Where(e => e.TargetElementName == "td" && e.Parent.Name == "tr");

var xpath = CreateXpath.Where(e => e.TargetElementName == "td").Select(e => e.Text);

不确定这是否有助于此上下文,但您可以在此处找到文档:

http://www.syntaxsuccess.com/viewarticle/how-to-create-xpath-using-linq

(注:该链接提供了有关如何使用LINQ创建XPath的信息。)

1
答案是肯定和否定的。 这是可能的,但不是使用XElement,因为XElement不能代表整个文档,只能代表其中的一部分。而表示整个XML文档的XDocument有一个名为XPathSelectElement()的函数,专门用于此目的。
所以对于像这样的XML文档
<response>
  <status_code>200</status_code>
  <status_txt>OK</status_txt>
  <data>
    <url>http://someurl/b47LVi</url>
    <hash>b47LVi</hash>
    <global_hash>9EJa3m</global_hash>
    <long_url>http://www.tumblr.com/docs/en/api#api_write</long_url>
    <new_hash>0</new_hash>
  </data>
</response>

你首先需要用 XDocument 读取它。
XDocument.Parse("xmlcontent..");

//convert from Xelement
var xe = XElement.Parse("xml string data...");
var xd = new XDocument(xe);

然后你可以调用XDocument.XPathSelectElement("/response/data/hash")来获取你的结果,如下面的示例所示:
void Main()
{
    var xm = @"<response>
  <status_code>200</status_code>
  <status_txt>OK</status_txt>
  <data>
    <url>http://someurl/b47LVi</url>
    <hash>b47LVi</hash>
    <global_hash>9EJa3m</global_hash>
    <long_url>http://www.tumblr.com/docs/en/api#api_write</long_url>
    <new_hash>0</new_hash>
  </data>
</response>";
    
    var xd = XDocument.Parse(xm);
    xd.XPathSelectElement("/response/data/hash").Dump("element");
    xd.XPathSelectElement("/response/data/hash").Value.Dump("value");

}

注意:`Dump()`是一种用于执行`Console.WriteLine()`的方法,请在您的代码中进行替换。
输出结果将类似于以下内容: enter image description here

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接