LINQ之路18:LINQ to XML之导航和查询
2014-10-22 21:34
274 查看
LINQ之路18:LINQ to XML之导航和查询
正如我们期望的那样,XNode和XContainer类定义了用于遍历X-DOM tree的方法和属性。但是和传统的DOM不同,这些方法并不返回IList<T>集合,而是返回单个值或者实现了IEnumerable<T>的sequence(这样我们就可以对其创建LINQ查询了)。本篇我们会讲述X-DOM的各种导航方法。
在目标对象列标记了*的函数同样可以应用于该目标对象sequence(本系列LINQ to XML博客中其他表格也一样)。例如,我们可以对一个XContainer或XContainer sequence调用Nodes()函数。作用于sequence的函数会把针对其中每个元素所得的结果连接起来。之所以可以这样工作,是因为定义在System.Xml.Linq中的扩展方法,即前面提到的补充查询运算符。
示例如下:
![](http://common.cnblogs.com/images/copycode.gif)
![](http://common.cnblogs.com/images/copycode.gif)
下面的LINQ查询查找包含”Nailgun”的toolbox:
下面的示例使用一个SelectMany查询来检索hand tools:
![](http://common.cnblogs.com/images/copycode.gif)
![](http://common.cnblogs.com/images/copycode.gif)
Elements方法等价于在Nodes上的一个LINQ查询,比如前一个示例中的Elements方法也可以用如下查询实现:
Elements也可以只返回给定名字的elements,如:
IEnumerable<XContainer>也定义了名为Elements的扩展方法,这让我们可以对一个element sequence调用Elements方法。所以,上面检索hand tools的SelectMany查询可以重写为如下形式:
第一个Elements方法绑定到XContainer的实例方法,而第二个则会调用IEnumerable<XContainer>中的扩展方法。
Element等价于在Elements()之后应用LINQ的FirstOrDefault查询运算符(指定一个name匹配条件)。如果查询的元素不存在,Element返回null。
如果xyz元素不存在,Element("xyz").Value会抛出空引用异常。如果我们希望得到一个null值而不是异常,则可以把XElement转为一个string,而不是查询它的Value属性:
这种方法之所以可行,是因为XElement定义了显示的到string类型的转换,为的就是这个目的。如果element存在,(string)element返回其Value属性,否则,返回null。
回到我们前面的例子,我们可以使用Descendants来找到所有的hand tools:
调用Descendants方法时,符合条件的parent和leaf nodes都会被包含进来, 如下所示:
![](http://common.cnblogs.com/images/copycode.gif)
![](http://common.cnblogs.com/images/copycode.gif)
下面的查询会检索出所有包含”careful”的XML注释,而不管该注释位于X-DOM之内的任何位置:
如果x是一个XElement,下面的语句总是输出true:
但如果x是一个XDocument,行为则有所不同。XDocument的特殊性在于:它可以有children,但它永远都不会成为其他node的parent!要存取XDocument,我们必须使用Document属性,它对X-DOM tree中的任意对象都有效。
Ancestors返回一个sequence,它的第一个element是Parent节点,下一个节点是Parent.Parent,依次类推,直到根节点。
我们可以通过如下的LINQ查询来得到根节点:AncestorsAndSelf().Last()。
另一种得到根节点的方法是调用Document.Root,当然这种方法只能在XDocument对象存在的情况下使用。
对于PreviousNode和NextNode(还有FirstNode/LastNode),我们可以像一个链表/linked list那样来遍历节点。实际上,nodes再内部实现时就是存放在链表中。XNode在内部使用的是单向链表,所以调用PreviousNode的性能不会很好。
![](http://common.cnblogs.com/images/copycode.gif)
![](http://common.cnblogs.com/images/copycode.gif)
对于其他方法就不再详细讨论了,方法名称就说明了一切。记得Node和Element的区别就行!
此外,XAttribute还定义了PreviousAttribute、NextAttribute和Parent属性。
带有一个name参数的Attributes方法返回一个sequence,其中会包含零个或一个元素,因为一个XML中的XElement不能拥有相同名字的attributes。
下一篇会和大家讨论X-DOM的更新技术
LINQ之路18:LINQ to XML之导航和查询
正如我们期望的那样,XNode和XContainer类定义了用于遍历X-DOM tree的方法和属性。但是和传统的DOM不同,这些方法并不返回IList<T>集合,而是返回单个值或者实现了IEnumerable<T>的sequence(这样我们就可以对其创建LINQ查询了)。本篇我们会讲述X-DOM的各种导航方法。
子节点导航/Child Node Navigation
返回类型 | 成员 | 目标对象 |
XNode | FirstNode { get; } | XContainer |
LastNode { get; } | XContainer | |
IEnumerable<XNode> | Nodes() | XContainer* |
DescendantNodes() | XContainer* | |
DescendantNodesAndSelf() | XElement* | |
XElement | Element (XName) | XContainer |
IEnumerable<XElement> | Elements() | XContainer* |
Elements (XName) | XContainer* | |
Descendants() | XContainer* | |
Descendants (XName) | XContainer* | |
DescendantsAndSelf() | XElement* | |
DescendantsAndSelf (XName) | XElement* | |
bool | HasElements { get; } | XElement |
FirstNode, LastNode, 和Nodes
FirstNode和LastNode可以让我们直接访问第一个和最后一个子节点;Nodes返回所有的子节点到一个sequence中。所有三个方法都只返回直接的后代节点。示例如下:
![](http://common.cnblogs.com/images/copycode.gif)
var bench = new XElement("bench", new XElement("toolbox", new XElement("handtool", "Hammer"), new XElement("handtool", "Rasp") ), new XElement("toolbox", new XElement("handtool", "Saw"), new XElement("powertool", "Nailgun") ), new 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-->
![](http://common.cnblogs.com/images/copycode.gif)
检索元素
Elements方法仅返回XElement类型的子节点:foreach (XElement e in bench.Elements()) Console.WriteLine(e.Name + "=" + e.Value); // 输出: toolbox=HammerRasp toolbox=SawNailgun
下面的LINQ查询查找包含”Nailgun”的toolbox:
IEnumerable<string> query = from toolbox in bench.Elements() where toolbox.Elements().Any (tool => tool.Value == "Nailgun") select toolbox.Value; RESULT: { "SawNailgun" }
下面的示例使用一个SelectMany查询来检索hand tools:
![](http://common.cnblogs.com/images/copycode.gif)
IEnumerable<string> query = from toolbox in bench.Elements() from tool in toolbox.Elements() where tool.Name == "handtool" select tool.Value; RESULT: { "Hammer", "Rasp", "Saw" }
![](http://common.cnblogs.com/images/copycode.gif)
Elements方法等价于在Nodes上的一个LINQ查询,比如前一个示例中的Elements方法也可以用如下查询实现:
from toolbox in bench.Nodes().OfType<XElement>() where ...
Elements也可以只返回给定名字的elements,如:
int x = bench.Elements ("toolbox").Count(); // 2 // 等价于 int x = bench.Elements().Where (e => e.Name == "toolbox").Count(); // 2
IEnumerable<XContainer>也定义了名为Elements的扩展方法,这让我们可以对一个element sequence调用Elements方法。所以,上面检索hand tools的SelectMany查询可以重写为如下形式:
IEnumerable<string> query = from tool in bench.Elements("toolbox").Elements("handtool") select tool.Value;
第一个Elements方法绑定到XContainer的实例方法,而第二个则会调用IEnumerable<XContainer>中的扩展方法。
检索单个元素
单数形式的Element方法返回匹配给定名称的第一个元素。Element对于单个元素的导航非常有用,示例如下:XElement settings = XElement.Load("databaseSettings.xml"); string cs = settings.Element("database").Element("connectString").Value;
Element等价于在Elements()之后应用LINQ的FirstOrDefault查询运算符(指定一个name匹配条件)。如果查询的元素不存在,Element返回null。
如果xyz元素不存在,Element("xyz").Value会抛出空引用异常。如果我们希望得到一个null值而不是异常,则可以把XElement转为一个string,而不是查询它的Value属性:
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:
Console.WriteLine(bench.Descendants("handtool").Count()); // 3
调用Descendants方法时,符合条件的parent和leaf nodes都会被包含进来, 如下所示:
![](http://common.cnblogs.com/images/copycode.gif)
foreach (XNode node 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-->
![](http://common.cnblogs.com/images/copycode.gif)
下面的查询会检索出所有包含”careful”的XML注释,而不管该注释位于X-DOM之内的任何位置:
IEnumerable<string> query = from c in bench.DescendantNodes().OfType<XComment>() where c.Value.Contains("careful") orderby c.Value select c.Value;
父节点导航/Parent Navigation
所有的XNodes都为父节点导航提供了Parent属性和AncestorXXX方法。一个Parent/父节点总是一个XElement:返回类型 | 成员 | 目标对象 |
XElement | Parent { get; } | XNode* |
Enumerable<XElement> | Ancestors() | XNode* |
Ancestors (XName) | XNode* | |
AncestorsAndSelf() | XElement* | |
AncestorsAndSelf (XName) | XElement* |
foreach (XNode child in x.Nodes()) 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<XNode> | NodesBeforeSelf() | XNode |
NodesAfterSelf() | XNode | |
IEnumerable<XElement> | ElementsBeforeSelf() | XNode |
ElementsBeforeSelf (XName name) | XNode | |
ElementsAfterSelf() | XNode | |
ElementsAfterSelf (XName name) | XNode |
![](http://common.cnblogs.com/images/copycode.gif)
XElement e = new XElement("customers", new XElement("customer", "Tom"), new XElement("customer", "Dick"), new XElement("customer", "Harry"), new XElement("customer", "Mary"), new XElement("customer", "Jay")); Console.WriteLine(e.ToString()); XElement current = e.Elements().Single(x => x.Value == "Harry"); bool flag1 = current.IsBefore(e.FirstNode); // false bool 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>
![](http://common.cnblogs.com/images/copycode.gif)
对于其他方法就不再详细讨论了,方法名称就说明了一切。记得Node和Element的区别就行!
属性导航/Attribute Navigation
返回类型 | 成员 | 目标对象 |
bool | HasAttributes { get; } | XElement |
XAttribute | Attribute (XName name) | XElement |
FirstAttribute { get; } | XElement | |
LastAttribute { get; } | XElement | |
IEnumerable<XAttribute> | Attributes() | XElement |
Attributes (XName name) | XElement |
带有一个name参数的Attributes方法返回一个sequence,其中会包含零个或一个元素,因为一个XML中的XElement不能拥有相同名字的attributes。
下一篇会和大家讨论X-DOM的更新技术
相关文章推荐
- LINQ之路18:LINQ to XML之导航和查询
- 用Linq to XML的方式查询XML 文档
- Linq to XML说法——(一)创建,添加,查询
- 步步为营VS 2008 + .NET 3.5(14) - XLINQ(LINQ to XML)之针对XML文件的添加、查询、更新和删除
- LINQ TO XML查询结果null 解决方法
- LINQ to XML 用 LINQ 查询 XML
- Linq to XML 排序,分页,多节点查询数据
- 步步为营VS 2008 + .NET 3.5(14) - XLINQ(LINQ to XML)之针对XML文件的添加、查询、更新和删除
- LinQ To XML——用LinQ查询XML
- XLINQ(LINQ to XML)之针对XML文件的添加、查询、更新和删除
- LINQ to XML 用 LINQ 查询 XML
- [代码]使用LINQ的查询结果填充XML树(LINQ to XML)
- linq to xml (linq查询)
- 步步为营VS 2008 + .NET 3.5(14) - XLINQ(LINQ to XML)之针对XML文件的添加、查询、更新和删除
- linq to sql取出随机记录/多表查询/将查询出的结果生成xml
- Linq To xml 查询时需要注意的一点。
- 用Linq to XML的方式查询XML 文档
- Linq To Xml实现类似XPath查询(Silverlight下的XPath)
- LINQ TO XML(查询)
- LINQ To XML:之复杂的查询举例