LINQ学习笔记:导航和查询X-DOM


导航/查询X-DOM

就像你可能期望的那样, XNode和XContainer类定义了方法和属性来测量X-DOM树. 与常规的DOM不同的是, 这些函数并不会返回实现了IList<T>的集合, 而是返回了一个单一值或者一个实现了IEnumerable<T>的序列. 基于这个你可以执行一个LINQ查询或者使用foreach来做枚举. 这同时也允许你使用熟悉的LINQ查询语法来执行简单的导航任务或者高级查询.

在X-DOM中, 元素和属性名都是大小写敏感的, 这与XML是一致的.

FirstNode, LastNode与Nodes

FirstNode与LastNode允许你直接访问第一个或者最后一个子节点; Nodes返回所有的子节点并形成一个序列. 这三个方法只用于直系的后代节点.

检索元素

Elements方法返回类型为XElement的子节点. 例如:

   1: var bench = new XElement ("bench",
   2:               new XElement ("toolbox",
   3:                 new XElement ("handtool", "Hammer"),
   4:                 new XElement ("handtool", "Rasp")
   5:               ),
   6:               new XElement ("toolbox",
   7:                 new XElement ("handtool", "Saw"),
   8:                 new XElement ("powertool", "Nailgun")
   9:               ),
  10:               new XComment ("Careful with the nailgun")
  11:             );
  12:
  13: foreach (XElement e in bench.Elements( ))
  14:   Console.WriteLine (e.Name + "=" + e.Value);
  15:
  16: // 结果: toolbox=HammerRasp
  17:            toolbox=SawNailgun

以下的LINQ查询用于查询包含nail gun的toolbox:

   1: IEnumerable<string> query =
   2:   from toolbox in bench.Elements( )
   3:   where toolbox.Elements( ).Any
   4:     (tool => tool.Value == "Nailgun")
   5:   select toolbox.Value;
   6:
   7: RESULT: { "SawNailgun" }

Elements等价于Nodes上面的LINQ查询, 我们之前的查询也可以被写为:

   1: from toolbox in bench.Nodes().OfType<XElement>()
   2: where ...

接下来的例子使用一个SelectMany查询检索hand tools:

   1: IEnumerable<string> query =
   2:   from toolbox in bench.Elements( )
   3:   from tool in toolbox.Elements( )
   4:   where tool.Name == "handtool"
   5:   select tool.Value;
   6:
   7: RESULT: { "Hammer", "Rasp", "Saw" }

Elements也可以只返回给定名称的元素, 例如:

   1: int x = bench.Elements ("toolbox").Count();      // 2

这等价于:

   1: int x = bench.Elements()
   2:              .Where (e => e.Name == "toolbox")
   3:              .Count();                                                  // 2
   4:

Elements还定义了一个扩展方法接受IEnumerable<XContainer>参数. 更精确的说, 它接受了此种类型的参数:

   1: IEnumerable<T> where T : XContainer

这让其可以和元素序列一起工作, 使用这个方法我们可以重写查找hand tools的查询:

   1: from tool in bench.Elements ("toolbox")
   2:                   .Elements ("handtool")
   3: select tool.Value.ToUpper( );

第一个Elements绑定到XContainer上, 第二个则绑定到扩展方法上.

读取一个单一的元素

方法Element(单数)返回匹配给定名称的第一个元素. Element对于简单的导航是非常有用的, 例如:

   1: var settings = XElement.Load ("databaseSettings.xml");
   2:
   3: string cx = settings.Element ("database")
   4:                     .Element ("connectString")
   5:                     .Value;

Element的作用相当于调用Elements然后再应用LINQ的FirstOrDefault并给定一个名称作为匹配断言. 如果没有任何元素匹配到, 则Element返回null.

如果元素xyz不存在, 那么Element(”xyz”).Value将会抛出一个NullReferenceException异常. 如果你倾向于使用null代替异常, 可以将XElement转换成string而不是调用它的Value属性, 如下:

   1: string xyz =
   2:   (string) settings.Element("xyz");

XElement定义了一个显示的string转换正式为了这个目的.

递归功能

XContainer同时也定义了Descendants和DescendantNodes方法, 它们递归地返回子元素或者子节点.Descendant接受一个可选的元素名, 会到我们之前的例子, 我们可以使用Descendants去查找所有的hand tools:

   1: Console.WriteLine
   2:   (bench.Descendants ("handtool").Count( ));   // 3

不管是父节点还是叶节点都包含在整体横切中. 以下的查询取得所有包含单词”careful”, 存在于X-DOM任何地方的注释节点:

   1: IEnumerable<string> query =
   2:   from c in bench.DescendantNodes( ).OfType<XComment>( )
   3:   where c.Value.Contains ("careful")
   4:   orderby c.Value
   5:   select c.Value;

查询父节点

所有的XNodes都有一个Parent属性和AncestorXX的方法用于父节点导航. 一个父亲节点永远是一个XElement.

如果x是一个XElement, 以下代码打印true:

   1: foreach (XNode child in x.Nodes( ))
   2:   Console.WriteLine (child.Parent == x);

如果x是一个XDocument的话, XDocument有点独特, 它永远不能作为任何节点的父亲节点. 为了访问XDocument,应该使用Document属性, 这只争对X-DOM树上的任意对象可以工作.

Ancestors返回一个序列其第一个元素是Parent, 下一个元素则是Parent.Parent, 依次类推直到根元素.

你还可以使用LINQ查询AncestorsAndSelf().Last()来取得输入序列的根元素. 另外一种方法是调用Document.Root, 但只有当XDocument呈现的时候才能工作.

对等节点导航

使用PreviousNode和NextNode(FirstNode / LastNode),我们可以使用一个linked list横贯所有的节点. 这并不是巧合, 在内部, Nodes实际上就是存储在一个linked list.

属性导航

XAttributes定义了PreviousAttribute和NextAttribute, Parent也是一样.

Attributes方法接受了一个名称并返回包含0或1个元素的序列; 在XML中, 一个元素不能包含重复的属性名.

待续!


« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3