找回密码
 立即注册
首页 业界区 业界 LINQ之路18:LINQ to XML之导航和查询

LINQ之路18:LINQ to XML之导航和查询

柏球侠 2025-5-29 16:01:57
正如我们期望的那样,XNode和XContainer类定义了用于遍历X-DOM tree的方法和属性。但是和传统的DOM不同,这些方法并不返回IList集合,而是返回单个值或者实现了IEnumerable的sequence(这样我们就可以对其创建LINQ查询了)。本篇我们会讲述X-DOM的各种导航方法。
子节点导航/Child Node Navigation

返回类型
成员
目标对象
XNode
FirstNode { get; }
XContainer
LastNode { get; }
XContainer
IEnumerable
Nodes()
XContainer*
DescendantNodes()
XContainer*
DescendantNodesAndSelf()
XElement*
XElement
Element (XName)
XContainer
IEnumerable
Elements()
XContainer*
Elements (XName)
XContainer*
Descendants()
XContainer*
Descendants (XName)
XContainer*
DescendantsAndSelf()
XElement*
DescendantsAndSelf (XName)
XElement*
bool
HasElements { get; }
XElement
在目标对象列标记了*的函数同样可以应用于该目标对象sequence(本系列LINQ to XML博客中其他表格也一样)。例如,我们可以对一个XContainer或XContainer sequence调用Nodes()函数。作用于sequence的函数会把针对其中每个元素所得的结果连接起来。之所以可以这样工作,是因为定义在System.Xml.Linq中的扩展方法,即前面提到的补充查询运算符。
FirstNode, LastNode, 和Nodes

FirstNode和LastNode可以让我们直接访问第一个和最后一个子节点;Nodes返回所有的子节点到一个sequence中。所有三个方法都只返回直接的后代节点。
示例如下:
  1.             var bench = new XElement("bench",<br>                new XElement("toolbox",<br>                    new XElement("handtool", "Hammer"),<br>                    new XElement("handtool", "Rasp")<br>                ),<br>                new XElement("toolbox",<br>                    new XElement("handtool", "Saw"),<br>                    new XElement("powertool", "Nailgun")<br>                ),<br>                new XComment("Be careful with the nailgun")<br>            );<br> <br>            foreach (XNode node in bench.Nodes())<br>                Console.WriteLine(node.ToString(SaveOptions.DisableFormatting));<br> <br>            //输出如下:<br>            <toolbox><handtool>Hammer</handtool><handtool>Rasp</handtool></toolbox><br>            <toolbox><handtool>Saw</handtool><powertool>Nailgun</powertool></toolbox><br>            
复制代码
 
检索元素

Elements方法仅返回XElement类型的子节点:
  1.             foreach (XElement e in bench.Elements())<br>                Console.WriteLine(e.Name + "=" + e.Value);<br> <br>            // 输出:<br>            toolbox=HammerRasp<br>            toolbox=SawNailgun
复制代码
下面的LINQ查询查找包含”Nailgun”的toolbox:
  1.             IEnumerable<string> query =<br>                from toolbox in bench.Elements()<br>                where toolbox.Elements().Any (tool => tool.Value == "Nailgun")<br>                select toolbox.Value;<br>           <br>            RESULT: { "SawNailgun" }
复制代码
下面的示例使用一个SelectMany查询来检索hand tools:
  1.             IEnumerable<string> query =<br>                from toolbox in bench.Elements()<br>                from tool in toolbox.Elements()<br>                where tool.Name == "handtool"<br>                select tool.Value;<br> <br>            RESULT: { "Hammer", "Rasp", "Saw" }
复制代码
Elements方法等价于在Nodes上的一个LINQ查询,比如前一个示例中的Elements方法也可以用如下查询实现:
  1.                 from toolbox in bench.Nodes().OfType<XElement>()<br>                where ...
复制代码
Elements也可以只返回给定名字的elements,如:
  1.             int x = bench.Elements ("toolbox").Count(); // 2<br>            // 等价于<br>            int x = bench.Elements().Where (e => e.Name == "toolbox").Count(); // 2
复制代码
IEnumerable也定义了名为Elements的扩展方法,这让我们可以对一个element sequence调用Elements方法。所以,上面检索hand tools的SelectMany查询可以重写为如下形式:
  1.             IEnumerable<string> query =<br>                from tool in bench.Elements("toolbox").Elements("handtool")<br>                select tool.Value;
复制代码
第一个Elements方法绑定到XContainer的实例方法,而第二个则会调用IEnumerable中的扩展方法。
检索单个元素

单数形式的Element方法返回匹配给定名称的第一个元素。Element对于单个元素的导航非常有用,示例如下:
  1.             XElement settings = XElement.Load("databaseSettings.xml");<br>            string cs = settings.Element("database").Element("connectString").Value;
复制代码
Element等价于在Elements()之后应用LINQ的FirstOrDefault查询运算符(指定一个name匹配条件)。如果查询的元素不存在,Element返回null。
如果xyz元素不存在,Element("xyz").Value会抛出空引用异常。如果我们希望得到一个null值而不是异常,则可以把XElement转为一个string,而不是查询它的Value属性:
  1.             string xyz = (string)settings.Element("xyz");
复制代码
这种方法之所以可行,是因为XElement定义了显示的到string类型的转换,为的就是这个目的。如果element存在,(string)element返回其Value属性,否则,返回null。
递归函数

XContainer还提供了Descendants和DescendantNodes方法,用来递归地返回child elements或nodes,即所有的后代elements或nodes。Descendants可选的接受一个element名称。
回到我们前面的例子,我们可以使用Descendants来找到所有的hand tools:
  1.             Console.WriteLine(bench.Descendants("handtool").Count()); // 3
复制代码
调用Descendants方法时,符合条件的parent和leaf nodes都会被包含进来, 如下所示:
  1.             foreach (XNode node in bench.DescendantNodes())<br>                Console.WriteLine(node.ToString(SaveOptions.DisableFormatting));<br> <br>            // 结果如下:<br>            <toolbox><handtool>Hammer</handtool><handtool>Rasp</handtool></toolbox><br>            <handtool>Hammer</handtool><br>            Hammer<br>            <handtool>Rasp</handtool><br>            Rasp<br>            <toolbox><handtool>Saw</handtool><powertool>Nailgun</powertool></toolbox><br>            <handtool>Saw</handtool><br>            Saw<br>            <powertool>Nailgun</powertool><br>            Nailgun<br>            
复制代码
下面的查询会检索出所有包含”careful”的XML注释,而不管该注释位于X-DOM之内的任何位置:
  1.             IEnumerable<string> query =<br>                from c in bench.DescendantNodes().OfType<XComment>()<br>                where c.Value.Contains("careful")<br>                orderby c.Value<br>                select c.Value;
复制代码
 
父节点导航/Parent Navigation

所有的XNodes都为父节点导航提供了Parent属性和AncestorXXX方法。一个Parent/父节点总是一个XElement:
返回类型
成员
目标对象
XElement
Parent { get; }
XNode*
Enumerable
Ancestors()
XNode*
Ancestors (XName)
XNode*
AncestorsAndSelf()
XElement*
AncestorsAndSelf (XName)
XElement*
如果x是一个XElement,下面的语句总是输出true:
  1.             foreach (XNode child in x.Nodes())<br>                Console.WriteLine(child.Parent == x);
复制代码
但如果x是一个XDocument,行为则有所不同。XDocument的特殊性在于:它可以有children,但它永远都不会成为其他node的parent!要存取XDocument,我们必须使用Document属性,它对X-DOM tree中的任意对象都有效。
Ancestors返回一个sequence,它的第一个element是Parent节点,下一个节点是Parent.Parent,依次类推,直到根节点。
我们可以通过如下的LINQ查询来得到根节点:AncestorsAndSelf().Last()。
另一种得到根节点的方法是调用Document.Root,当然这种方法只能在XDocument对象存在的情况下使用。
 
兄弟节点导航/Peer Node Navigation

返回类型
成员
目标对象
bool
IsBefore (XNode node)
XNode
IsAfter (XNode node)
XNode
XNode
PreviousNode { get; }
XNode
NextNode { get; }
XNode
IEnumerable
NodesBeforeSelf()
XNode
 
NodesAfterSelf()
XNode
IEnumerable
ElementsBeforeSelf()
XNode
ElementsBeforeSelf (XName name)
XNode
ElementsAfterSelf()
XNode
ElementsAfterSelf (XName name)
XNode
对于PreviousNode和NextNode(还有FirstNode/LastNode),我们可以像一个链表/linked list那样来遍历节点。实际上,nodes再内部实现时就是存放在链表中。XNode在内部使用的是单向链表,所以调用PreviousNode的性能不会很好。
  1.             XElement e = new XElement("customers",<br>                new XElement("customer", "Tom"),<br>                new XElement("customer", "Dick"),<br>                new XElement("customer", "Harry"),<br>                new XElement("customer", "Mary"),<br>                new XElement("customer", "Jay"));<br>            Console.WriteLine(e.ToString());<br> <br>            XElement current = e.Elements().Single(x => x.Value == "Harry");<br>            bool flag1 = current.IsBefore(e.FirstNode);   // false<br>            bool flag2 = current.IsAfter(e.FirstNode);   // true<br>            XNode pNode = current.PreviousNode;            // <customer>Dick</customer><br>            XNode nNode = current.NextNode;                // <customer>Mary</customer><br>            var query = current.NodesBeforeSelf();  // <customer>Tom</customer> & <customer>Dick</customer>
复制代码
对于其他方法就不再详细讨论了,方法名称就说明了一切。记得Node和Element的区别就行!
 
属性导航/Attribute Navigation

返回类型
成员
目标对象
bool
HasAttributes { get; }
XElement
XAttribute
Attribute (XName name)
XElement
FirstAttribute { get; }
XElement
LastAttribute { get; }
XElement
IEnumerable
Attributes()
XElement
Attributes (XName name)
XElement
此外,XAttribute还定义了PreviousAttribute、NextAttribute和Parent属性。
带有一个name参数的Attributes方法返回一个sequence,其中会包含零个或一个元素,因为一个XML中的XElement不能拥有相同名字的attributes。
下一篇会和大家讨论X-DOM的更新技术
 
系列博客导航:
LINQ之路系列博客导航
LINQ之路 1:LINQ介绍
LINQ之路 2:C# 3.0的语言功能(上)
LINQ之路 3:C# 3.0的语言功能(下)
LINQ之路 4:LINQ方法语法
LINQ之路 5:LINQ查询表达式
LINQ之路 6:延迟执行(Deferred Execution)
LINQ之路 7:子查询、创建策略和数据转换
LINQ之路 8:解释查询(Interpreted Queries)
LINQ之路 9:LINQ to SQL 和 Entity Framework(上)
LINQ之路10:LINQ to SQL 和 Entity Framework(下)
LINQ之路11:LINQ Operators之过滤(Filtering)
LINQ之路12:LINQ Operators之数据转换(Projecting)
LINQ之路13:LINQ Operators之连接(Joining)
LINQ之路14:LINQ Operators之排序和分组(Ordering and Grouping)
LINQ之路15:LINQ Operators之元素运算符、集合方法、量词方法
LINQ之路16:LINQ Operators之集合运算符、Zip操作符、转换方法、生成器方法
LINQ之路17:LINQ to XML之X-DOM介绍
LINQ之路18:LINQ to XML之导航和查询
LINQ之路19:LINQ to XML之X-DOM更新、和Value属性交互
LINQ之路20:LINQ to XML之Documents、Declarations和Namespaces
LINQ之路21:LINQ to XML之生成X-DOM(Projecting)
LINQ之路系列博客后记
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册