我有一个XDocument
对象。我想使用LINQ查询任何深度上具有特定名称的元素。
当我使用Descendants("element_name")
时,我只能得到当前级别的直接子元素。我正在寻找与XPath中的"//element_name"等效的方法...我应该只使用XPath
,还是有一种方法可以使用LINQ方法进行操作?
我有一个XDocument
对象。我想使用LINQ查询任何深度上具有特定名称的元素。
当我使用Descendants("element_name")
时,我只能得到当前级别的直接子元素。我正在寻找与XPath中的"//element_name"等效的方法...我应该只使用XPath
,还是有一种方法可以使用LINQ方法进行操作?
后代选择器应该完全没有问题。这是一个例子:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
string xml = @"
<root>
<child id='1'/>
<child id='2'>
<grandchild id='3' />
<grandchild id='4' />
</child>
</root>";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants("grandchild"))
{
Console.WriteLine(element);
}
}
}
结果:
<grandchild id="3" />
<grandchild id="4" />
一个指示命名空间的示例:
String TheDocumentContent =
@"
<TheNamespace:root xmlns:TheNamespace = 'http://www.w3.org/2001/XMLSchema' >
<TheNamespace:GrandParent>
<TheNamespace:Parent>
<TheNamespace:Child theName = 'Fred' />
<TheNamespace:Child theName = 'Gabi' />
<TheNamespace:Child theName = 'George'/>
<TheNamespace:Child theName = 'Grace' />
<TheNamespace:Child theName = 'Sam' />
</TheNamespace:Parent>
</TheNamespace:GrandParent>
</TheNamespace:root>
";
XDocument TheDocument = XDocument.Parse( TheDocumentContent );
//Example 1:
var TheElements1 =
from
AnyElement
in
TheDocument.Descendants( "{http://www.w3.org/2001/XMLSchema}Child" )
select
AnyElement;
ResultsTxt.AppendText( TheElements1.Count().ToString() );
//Example 2:
var TheElements2 =
from
AnyElement
in
TheDocument.Descendants( "{http://www.w3.org/2001/XMLSchema}Child" )
where
AnyElement.Attribute( "theName" ).Value.StartsWith( "G" )
select
AnyElement;
foreach ( XElement CurrentElement in TheElements2 )
{
ResultsTxt.AppendText( "\r\n" + CurrentElement.Attribute( "theName" ).Value );
}
您可以这样做:
xml.Descendants().Where(p => p.Name.LocalName == "Name of the node to find")
其中 xml
是一个 XDocument
对象。
请注意,属性Name
返回一个对象,该对象具有LocalName
和Namespace
。这就是为什么如果您想按名称进行比较,必须使用 Name.LocalName
。
Descendants会完全按照您的需求执行操作,但请确保在元素名称中包括命名空间名称。如果省略它,您可能会得到一个空列表。
有两种方法可以实现这一点,
以下是使用这些方法的示例,
List<XElement> result = doc.Root.Element("emails").Elements("emailAddress").ToList();
IEnumerable<XElement> mails = ((IEnumerable)doc.XPathEvaluate("/emails/emailAddress")).Cast<XElement>();
var res = doc.XPathEvaluate("/emails/emailAddress");
结果可能是一个空指针,也可能没有结果。
XPathEvaluate
在System.Xml.XPath
命名空间中。 - Tahir Hassan我正在使用XPathSelectElements
扩展方法,它的工作方式与XmlDocument.SelectNodes
方法相同:
using System;
using System.Xml.Linq;
using System.Xml.XPath; // for XPathSelectElements
namespace testconsoleApp
{
class Program
{
static void Main(string[] args)
{
XDocument xdoc = XDocument.Parse(
@"<root>
<child>
<name>john</name>
</child>
<child>
<name>fred</name>
</child>
<child>
<name>mark</name>
</child>
</root>");
foreach (var childElem in xdoc.XPathSelectElements("//child"))
{
string childName = childElem.Element("name").Value;
Console.WriteLine(childName);
}
}
}
}
XDocument
类的Descendants方法的解决方案变体。using System;
using System.Linq;
using System.Xml.Linq;
class Test
{
static void Main()
{
XDocument xml = XDocument.Parse(@"
<root>
<child id='1'/>
<child id='2'>
<subChild id='3'>
<extChild id='5' />
<extChild id='6' />
</subChild>
<subChild id='4'>
<extChild id='7' />
</subChild>
</child>
</root>");
xml.Descendants().Where(p => p.Name.LocalName == "extChild")
.ToList()
.ForEach(e => Console.WriteLine(e));
Console.ReadLine();
}
}
在Francisco Goldenstein的回答后,我编写了一个扩展方法。
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Mediatel.Framework
{
public static class XDocumentHelper
{
public static IEnumerable<XElement> DescendantElements(this XDocument xDocument, string nodeName)
{
return xDocument.Descendants().Where(p => p.Name.LocalName == nodeName);
}
}
}
我们知道上面的话是真的。Jon从来不会错;现实生活中的愿望可以更进一步。
<ota:OTA_AirAvailRQ
xmlns:ota="http://www.opentravel.org/OTA/2003/05" EchoToken="740" Target=" Test" TimeStamp="2012-07-19T14:42:55.198Z" Version="1.1">
<ota:OriginDestinationInformation>
<ota:DepartureDateTime>2012-07-20T00:00:00Z</ota:DepartureDateTime>
</ota:OriginDestinationInformation>
</ota:OTA_AirAvailRQ>
您可以通过以下方式来访问带有命名空间和名称的元素:
doc.Descendants().Where(p => p.Name.LocalName == "OTA_AirAvailRQ").Attributes("EchoToken").FirstOrDefault().Value
您可以通过属性内容值来查找,就像这个。
(代码和指令适用于C#,但可能需要稍微修改以适用于其他语言)
如果您想从具有许多子节点的父节点中读取,例如查看以下XML,则此示例非常完美;
<?xml version="1.0" encoding="UTF-8"?>
<emails>
<emailAddress>jdoe@set.ca</emailAddress>
<emailAddress>jsmith@hit.ca</emailAddress>
<emailAddress>rgreen@set_ig.ca</emailAddress>
</emails>
XDocument doc = XDocument.Parse(Properties.Resources.EmailAddresses);
var emailAddresses = (from emails in doc.Descendants("emailAddress")
select emails.Value);
foreach (var email in emailAddresses)
{
//Comment out if using WPF or Windows Form project
Console.WriteLine(email.ToString());
//Remove comment if using WPF or Windows Form project
//MessageBox.Show(email.ToString());
}
注意:对于控制台应用程序和WPF或Windows Forms,您必须在项目顶部添加“using System.Xml.Linq;”Using指令,在添加Using指令之前,您还需要为控制台添加对此命名空间的引用。此外,对于控制台,默认情况下不会在“属性文件夹”下有资源文件,因此您必须手动添加资源文件。下面的MSDN文章详细解释了这一点。
doc.Descendants("Cars").Descendants("Part")
(如果它们仅是直接子节点,则可能使用.Elements("Part")
)。 - Jon Skeetvar foo = new XDocument().Descendants("Bar").Descendants("Baz");
,因为Descendants
返回一个空的IEnumerable<XElement>
而不是null
。 - DareDude