通过任意深度的名称查询XDocument的元素
我有一个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
如果你想比较名称。
后代将完全按照您的需要进行,但请确保您已将名称空间名称与元素名称一起包含在内。 如果你忽略它,你可能会得到一个空的列表。
有两种方法可以做到这一点,
- LINQ到XML
- XPath的
以下是使用这些方法的样本,
List<XElement> result = doc.Root.Element("emails").Elements("emailAddress").ToList();
如果您使用XPath,则需要使用IEnumerable进行一些操作:
IEnumerable<XElement> mails = ((IEnumerable)doc.XPathEvaluate("/emails/emailAddress")).Cast<XElement>();
注意
var res = doc.XPathEvaluate("/emails/emailAddress");
结果要么是空指针,要么没有结果。
我正在使用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); } } } }
继@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); } } }
(代码和指令是针对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>
现在下面的代码(请记住,XML文件存储在资源中(请参阅代码段末尾的资源帮助链接)您可以获取“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()); }
结果
- jdoe@set.ca
- jsmith@hit.ca
- rgreen@set_ig.ca
注意:对于控制台应用程序和WPF或Windows窗体,您必须添加“using System.Xml.Linq;” 在项目顶部使用指令,对于控制台,您还需要在添加Using指令之前添加对此名称空间的引用。 另外,对于控制台,在“属性文件夹”下默认情况下将没有资源文件,因此您必须手动添加资源文件。 下面的MSDN文章,详细解释这一点。
添加和编辑资源
如何添加或删除资源