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


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

在目标对象列标记了*的函数同样可以应用于该目标对象sequence(本系列LINQ to XML博客中其他表格也一样)。例如,我们可以对一个XContainer或XContainer sequence调用Nodes()函数。作用于sequence的函数会把针对其中每个元素所得的结果连接起来。之所以可以这样工作,是因为定义在System.Xml.Linq中的扩展方法,即前面提到的补充查询运算符。

FirstNode,LastNode,和Nodes

FirstNode和LastNode可以让我们直接访问第一个和最后一个子节点;Nodes返回所有的子节点到一个sequence中。所有三个方法都只返回直接的后代节点。

示例如下:

            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-->

检索元素

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 == ")
                select toolbox.Value;
           
            RESULT: { SawNailgun" }

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

            IEnumerable<from tool in toolbox.Elements()
                where tool.Name == "
                select tool.Value;
 
            RESULT: { " }

Elements方法等价于在Nodes上的一个LINQ查询,比如前一个示例中的Elements方法也可以用如下查询实现:

                in bench.Nodes().OfType<XElement>()
                where ...

Elements也可以只返回给定名字的elements,如:

            int x = bench.Elements (").Count();  2
             等价于
            int x = bench.Elements().Where (e => e.Name ==  2

IEnumerable<XContainer>也定义了名为Elements的扩展方法,这让我们可以对一个element sequence调用Elements方法。所以,上面检索hand tools的SelectMany查询可以重写为如下形式:

            IEnumerable<in bench.Elements(").Elements(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(").Count());  3

调用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-->

下面的查询会检索出所有包含”careful”的XML注释,而不管该注释位于X-DOM之内的任何位置:

            IEnumerable<from c in bench.DescendantNodes().OfType<XComment>()
                where c.Value.Contains(carefulorderby 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*

如果x是一个XElement,下面的语句总是输出true:

            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

对于PreviousNode和NextNode(还有FirstNode/LastNode),我们可以像一个链表/linked list那样来遍历节点。实际上,nodes再内部实现时就是存放在链表中。XNode在内部使用的是单向链表,所以调用PreviousNode的性能不会很好。

            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>

对于其他方法就不再详细讨论了,方法名称就说明了一切。记得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

此外,XAttribute还定义了PreviousAttribute、NextAttribute和Parent属性。

带有一个name参数的Attributes方法返回一个sequence,其中会包含零个或一个元素,因为一个XML中的XElement不能拥有相同名字的attributes。

下一篇会和大家讨论X-DOM的更新技术

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


php输出xml格式字符串
J2ME Mobile 3D入门教程系列文章之一
XML轻松学习手册
XML入门的常见问题(一)
XML入门的常见问题(三)
XML轻松学习手册(2)XML概念
xml文件介绍及使用
xml编程(一)-xml语法
XML文件结构和基本语法
第2章 包装类
XML入门的常见问题(二)
Java对象的强、软、弱和虚引用
JS解析XML文件和XML字符串详解
java中枚举的详细使用介绍
了解Xml格式
XML入门的常见问题(四)
深入SQLite多线程的使用总结详解
PlayFramework完整实现一个APP(一)
XML和YAML的使用方法
XML轻松学习总节篇