@H_
403_6@正如我们期望的那样,XNode和XContainer类定义了用于遍历X-DOM tree的
方法和
属性。但是和传统的DOM不同,这些
方法并不返回IList<T>集合,而是返回单个值或者实现了IEnumerable<T>的sequence(这样我们就可以对其创建LINQ
查询了)。本篇我们会讲述X-DOM的各种导航
方法。
子节点导航/Child Node Navigation
@H_403_6@返回类型 |
@H_403_6@成员 |
@H_403_6@目标对象 |
@H_403_6@XNode |
@H_403_6@FirstNode { get; } |
@H_403_6@XContainer |
@H_403_6@LastNode { get; } |
@H_403_6@XContainer |
@H_403_6@IEnumerable<XNode> |
@H_403_6@Nodes() |
@H_403_6@XContainer* |
@H_403_6@DescendantNodes() |
@H_403_6@XContainer* |
@H_403_6@DescendantNodesAndSelf() |
@H_403_6@XElement* |
@H_403_6@XElement |
@H_403_6@Element (XName) |
@H_403_6@XContainer |
@H_403_6@IEnumerable<XElement> |
@H_403_6@Elements() |
@H_403_6@XContainer* |
@H_403_6@Elements (XName) |
@H_403_6@XContainer* |
@H_403_6@Descendants() |
@H_403_6@XContainer* |
@H_403_6@Descendants (XName) |
@H_403_6@XContainer* |
@H_403_6@DescendantsAndSelf() |
@H_403_6@XElement* |
@H_403_6@DescendantsAndSelf (XName) |
@H_403_6@XElement* |
@H_403_6@bool |
@H_403_6@HasElements { get; } |
@H_403_6@XElement |
@H_
403_6@在目标对象列
标记了*的
函数同样可以应用于该目标对象sequence(本系列LINQ to XML
博客中其他表格也一样)。例如,我们可以对一个XContainer或XContainer sequence
调用Nodes()
函数。作用于sequence的
函数会把针对其中每个元素所得的结果连接起来。之所以可以这样工作,是因为定义在System.Xml.Linq中的扩展
方法,即前面提到的补充
查询运算符。
FirstNode,LastNode,和Nodes
@H_
403_6@FirstNode和LastNode可以让我们直接访问第一个和最后一个子节点;Nodes返回所有的子节点到一个sequence中。
所有三个方法都只返回直接的后代节点。
@H_
403_6@示例如下:
var bench = new XElement("bench",toolBoxhandtoolHammer"),0)">Rasp")
),0)">SawpowertoolNailgunnew XComment(Be careful with the nailgun")
);
foreach (XNode node in bench.Nodes())
Console.WriteLine(node.ToString(SaveOptions.DisableFormatting));
//输出如下:
<toolBox><handtool>Hammer</handtool><handtool>Rasp</handtool></toolBox>
<toolBox><handtool>Saw</handtool><powertool>Nailgun</powertool></toolBox>
<!--Be careful with the nailgun-->
检索元素
@H_
403_6@
Elements方法仅返回XElement类型的子节点:
foreach (XElement e in bench.Elements())
Console.WriteLine(e.Name + =" + e.Value);
输出:
toolBox=HammerRasp
toolBox=SawNailgun
@H_
403_6@下面的LINQ
查询查找包含”Nailgun”的tool
Box:
IEnumerable<string> query =
from toolBox in bench.Elements()
where toolBox.Elements().Any (tool => tool.Value == ")
select toolBox.Value;
RESULT: { SawNailgun" }
@H_
403_6@下面的示例使用一个SelectMany
查询来检索hand tools:
IEnumerable<from tool in toolBox.Elements()
where tool.Name == "
select tool.Value;
RESULT: { " }
@H_
403_6@Elements
方法等价于在Nodes上的一个LINQ
查询,比如前一个示例中的Elements
方法也可以用如下
查询实现:
in bench.Nodes().OfType<XElement>()
where ...
@H_
403_6@Elements也可以只返回给定名字的elements,如:
int x = bench.Elements (").Count(); 2
等价于
int x = bench.Elements().Where (e => e.Name == 2
@H_
403_6@
IEnumerable<XContainer>也定义了名为Elements的扩展方法,这让我们可以对一个element sequence调用Elements方法。所以,上面检索hand tools的SelectMany
查询可以重写为如下形式:
IEnumerable<in bench.Elements(").Elements(select tool.Value;
@H_
403_6@第一个Elements
方法绑定到XContainer的实例
方法,而第二个则会
调用IEnumerable<XContainer>中的扩展
方法。
检索单个元素
@H_
403_6@单数形式的Element
方法返回匹配给定
名称的第一个元素。Element对于单个元素的导航非常有用,示例如下:
XElement settings = XElement.Load(databaseSettings.xml");
string cs = settings.Element(database").Element(connectString").Value;
@H_
403_6@Element等价于在Elements()之后应用LINQ的FirstOrDefault
查询运算符(指定一个name匹配条件)。如果
查询的元素不存在,Element返回null。
@H_
403_6@如果xyz元素不存在,Element("xyz").Value会抛出空引用异常。如果我们希望得到一个null值而不是异常,则可以把XElement转为一个string,而不是
查询它的Value
属性:
string xyz = (string)settings.Element(xyz");
@H_
403_6@这种
方法之所以可行,是
因为XElement定义了显示的到string类型的转换,为的就是这个目的。如果element存在,(string)element返回其Value
属性,否则,返回null。
@H_
403_6@XContainer还提供了
Descendants和DescendantNodes方法,用来递归地返回child elements或nodes,即所有的后代elements或nodes。Descendants可选的接受一个element
名称。
@H_
403_6@回到我们前面的例子,我们可以使用Descendants来找到所有的hand tools:
Console.WriteLine(bench.Descendants(").Count()); 3
@H_
403_6@
调用Descendants
方法时,符合条件的parent和leaf nodes都会被包含进来, 如下所示:
in bench.DescendantNodes())
Console.WriteLine(node.ToString(SaveOptions.DisableFormatting));
结果如下:
<toolBox><handtool>Hammer</handtool><handtool>Rasp</handtool></toolBox>
<handtool>Hammer</handtool>
Hammer
<handtool>Rasp</handtool>
Rasp
<toolBox><handtool>Saw</handtool><powertool>Nailgun</powertool></toolBox>
<handtool>Saw</handtool>
Saw
<powertool>Nailgun</powertool>
Nailgun
<!--Be careful with the nailgun-->
@H_
403_6@下面的
查询会检索出所有包含”careful”的XML注释,而不管该注释位于X-DOM之内的任何位置:
IEnumerable<from c in bench.DescendantNodes().OfType<XComment>()
where c.Value.Contains(carefulorderby c.Value
select c.Value;
父节点导航/Parent Navigation
@H_
403_6@所有的XNodes都为父节点导航提供了Parent
属性和AncestorXXX
方法。
一个Parent/父节点总是一个XElement:
@H_403_6@返回类型 |
@H_403_6@成员 |
@H_403_6@目标对象 |
@H_403_6@XElement |
@H_403_6@Parent { get; } |
@H_403_6@XNode* |
@H_403_6@Enumerable<XElement> |
@H_403_6@Ancestors() |
@H_403_6@XNode* |
@H_403_6@Ancestors (XName) |
@H_403_6@XNode* |
@H_403_6@AncestorsAndSelf() |
@H_403_6@XElement* |
@H_403_6@AncestorsAndSelf (XName) |
@H_403_6@XElement* |
@H_
403_6@如果x是一个XElement,下面的语句总是
输出true:
foreach (XNode child in x.Nodes())
Console.WriteLine(child.Parent == x);
@H_
403_6@但如果x是一个XDocument,行为则有所不同。
XDocument的特殊性在于:它可以有children,但它永远都不会成为其他node的parent!要存取XDocument,我们必须使用Document属性,它对X-DOM tree中的任意对象都有效。
@H_
403_6@Ancestors返回一个sequence,它的第一个element是Parent节点,下一个节点是Parent.Parent,依次类推,直到根节点。
@H_
403_6@我们可以通过如下的LINQ
查询来得到根节点:AncestorsAndSelf().Last()。
@H_
403_6@另一种得到根节点的
方法是
调用Document.Root,当然这种
方法只能在XDocument对象存在的情况下使用。
兄弟节点导航/Peer Node Navigation
@H_403_6@返回类型 |
@H_403_6@成员 |
@H_403_6@目标对象 |
@H_403_6@bool |
@H_403_6@IsBefore (XNode node) |
@H_403_6@XNode |
@H_403_6@IsAfter (XNode node) |
@H_403_6@XNode |
@H_403_6@XNode |
@H_403_6@PrevIoUsNode { get; } |
@H_403_6@XNode |
@H_403_6@NextNode { get; } |
@H_403_6@XNode |
@H_403_6@IEnumerable<XNode> |
@H_403_6@NodesBeforeSelf() |
@H_403_6@XNode |
@H_403_6@ |
@H_403_6@NodesAfterSelf() |
@H_403_6@XNode |
@H_403_6@IEnumerable<XElement> |
@H_403_6@ElementsBeforeSelf() |
@H_403_6@XNode |
@H_403_6@ElementsBeforeSelf (XName name) |
@H_403_6@XNode |
@H_403_6@ElementsAfterSelf() |
@H_403_6@XNode |
@H_403_6@ElementsAfterSelf (XName name) |
@H_403_6@XNode |
@H_
403_6@对于Prev
IoUsNode和NextNode(还有FirstNode/LastNode),我们可以像一个链表/linked list那样来遍历节点。实际上,nodes再内部实现时就是存放在链表中。XNode在内部使用的是单向链表,所以
调用Prev
IoUsNode的
性能不会很好。
XElement e = customerscustomerTomDickHarryMaryJay"));
Console.WriteLine(e.ToString());
XElement current = e.Elements().Single(x => x.Value == bool flag1 = current.IsBefore(e.FirstNode); falsebool flag2 = current.IsAfter(e.FirstNode); true
XNode pNode = current.PrevIoUsNode; <customer>Dick</customer>
XNode nNode = current.NextNode; <customer>Mary</customer>var query = current.NodesBeforeSelf(); <customer>Tom</customer> & <customer>Dick</customer>
@H_
403_6@对于其他
方法就不再详细讨论了,
方法名称就说明了一切。记得Node和Element的区别就行!
属性导航/Attribute Navigation
@H_403_6@返回类型 |
@H_403_6@成员 |
@H_403_6@目标对象 |
@H_403_6@bool |
@H_403_6@HasAttributes { get; } |
@H_403_6@XElement |
@H_403_6@XAttribute |
@H_403_6@Attribute (XName name) |
@H_403_6@XElement |
@H_403_6@FirstAttribute { get; } |
@H_403_6@XElement |
@H_403_6@LastAttribute { get; } |
@H_403_6@XElement |
@H_403_6@IEnumerable<XAttribute> |
@H_403_6@Attributes() |
@H_403_6@XElement |
@H_403_6@Attributes (XName name) |
@H_403_6@XElement |
@H_
403_6@此外,XAttribute还定义了Prev
IoUsAttribute、NextAttribute和Parent
属性。
@H_
403_6@带有一个name参数的Attributes
方法返回一个sequence,其中会包含零个或一个元素,因为一个XML中的XElement不能拥有相同名字的attributes。
@H_
403_6@
下一篇会和大家讨论X-DOM的更新技术