LINQ to XML之X-DOM介绍

LINQ to XML之X-DOM介绍 NET Framework提供了数种操作XML数据的API。从Framework 3.5开始最重要的用来处理XML文档的技术当属LINQ to XML。LINQ to XML由一个轻量级的XML文档对象模型和一组补充查询运算符组成并且该文档对象模型是LINQ友好的。多数情况下它可以完全取代XML技术的前身符合W3C规则的DOM如XmlDocument。现在就让我们一起开始LINQ to XML的学习之旅看看它是怎样简化XML的查询与操作提高我们的工作效率的。所有的LINQ to XML类型都定义在System.Xml.Linq命名空间中请记住在运行相关示例时导入该命名空间。架构预览认识DOM让我们先来简单认识一下DOM的概念然后再来解释LINQ to XML’s DOM背后的基本原理。考虑下面的XML文件?xml version1.0 encodingutf-8 standaloneyes? customer id123 statusarchived firstnameJoe/firstname lastnameBloggs/lastname /customer和所有的XML文件一样我们从一个XML声明开始然后是root元素元素名为customer。它有2个attributes名字分别为id和status值分别为”123”和”archived”。在customer元素之内包含了两个子元素firstname和lastname分别包含一个文本内容 (Joe和Bloggs)。上面的每一种结构declaration, element, attribute, value, 和text content都可以用一个相应的类来表示。如果为这些类加上某些集合属性来存储子内容那么我们就可以通过一个对象树来完整的描述一个文档这就是文档对象模型简称DOM。LINQ to XML文档对象模型DOMLINQ to XML由下面两部分内容组成一个XML DOM我们可以称为X-DOM一组大约10个补充查询运算符X-DOM包含的类型有XDocument、XElement和XAttribute等。有意思的是X-DOM并不是和LINQ绑定在一起的我们可以撇开LINQ查询而单独装载、实例化、更新和保存X-DOM类型。相应的我们可以使用LINQ来查询老的W3C式样的XML类型当然这会有很多限制。X-DOM的重要特征是它是LINQ友好的这意味着它提供了产生IEnumerable sequences的方法然后我们就可以在sequence之上来建立查询。它的构造函数允许我们通过一个LINQ数据转换来创建一个X-DOM树X-DOM介绍下图显示了X-DOM的核心类型。使用最频繁的类型当属XElement。XObject是该继承层次的根类型XElement和XDocument是具体的容器类型。XObjectXObject所有XML数据的抽象基类它定义了Parent element和XDocument。XNodeXNode是大部分XML数据的基类除了XAttribute。一个XNode可以位于一个融合了多种XNode类型的有序集合之中比如下面的XMLdata Hello world subelement1/ !--comment-- subelement2/ /data在父元素data之内首先是一个XText node (Hello world)然后是一个XElement node然后是XComment node最后是第二个XElement node。尽管一个XNode可以访问它的父元素但是它并没有子节点/child nodes的概念。子节点由XContainer类提供。XNode除了包括XElement它还包含了XText、XComment等类型节点见上图在后面讲述X-DOM导航时你会看到各种XXXNodes和XXXElements方法请记得这个区别。XContainerXContainer定义了处理子节点的方法它是XElement和XDocument的抽象基类。XElementXElement包含了管理属性的方法以及Name和Value成员。并且由于它的基类是XContainer所以它可以包含子节点。通常情况下一个element会有单个XText子节点并且Value属性封装了对该XText子节点的操作。所以多数情况下我们并不需要直接和XText打交道。XDocumentXDocument表示了XML树的根节点。它包装了root XElement、一个XDeclaration、processing instructions、和其他根级类型对象。和W3C DOM不同的是对XDocument的使用是可选的我们可以在不创建XDocument的情况下装载、操作和保存一个X-DOM对XDocument的独立性甚至意味着我们可以高效的把一个node子树移动到另一个不相关的X-DOM层次对象中去。Loading和ParsingXelement和XDocument都提供了静态的Load和Parse方法他们用来创建X-DOM tree。Load用来从file、URI、Stream、TextReader或XmlReader创建X-DOMParse用来从string创建X-DOM示例如下XDocument fromWeb XDocument.Load(http://albahari.com/sample.xml); XElement fromFile XElement.Load(e:\media\somefile.xml); XElement config XElement.Parse( configuration client enabledtrue timeout30/timeout /client /configuration);保存和序列化调用任意node的ToString()方法都会把它的内容转换到一个XML string该string由分行符和缩进格式化当然我们可以通过SaveOptions.DisableFormatting属性来取消分行符合缩进。XElement和XDocument还提供了Save方法来把一个X-DOM写到一个file, Stream, TextWriter, 或 XmlWriter。如果指定的是一个file则会自动添加一个XML declaration。对XML declarations的详细介绍会在之后给出。实例化X-DOM除了使用Load和Parse方法我们还可以通过实例化对象并把他们添加至父节点来创建X-DOM tree。// 构建XElement和XAttribute时只需提供name和value XElement lastName new XElement(lastname, Bloggs); lastName.Add(new XComment(nice name)); XElement customer new XElement(customer); customer.Add(new XAttribute(id, 123)); customer.Add(new XElement(firstname, Joe)); customer.Add(lastName); Console.WriteLine(customer.ToString());结果如下customer id123 firstnameJoe/firstname lastnameBloggs!--nice name--/lastname /customer创建XElement时value参数是可选的我们可以只指定name而在此之后再添加内容。当我们提供了value属性时一个简单的string就可以了XDOM会自动将其转换为XText子节点。函数式构造/Functional Construction在上一个例子中我们很难从代码中看出XML的结构。现在XDOM支持了另外一种实例化模式函数式构造由函数式编程而来。通过函数式构造我们在一个表达式中就可以创建完整的XDOM treeXElement customer new XElement(customer, new XAttribute(id, 123), new XElement(firstname, joe), new XElement(lastname, bloggs, new XComment(nice name) ) );这样有两个优势。首先代码反映了XML的结构其次它可以与LINQ查询的select子句进行交互。比如下面的LINQ to SQL查询直接把结果转换到一个X-DOM:XElement query new XElement(customers, from c in dataContext.Customers select new XElement(customer, new XAttribute(id, c.ID), new XElement(firstname, c.FirstName), new XElement(lastname, c.LastName, new XComment(nice name) ) ) );这种技术的详细信息会在后续篇章中再作介绍。函数式构造工作方式函数式构造之所以可行是因为XElement和XDocument的构造函数提供了如下的重载形式// params object[]意味着可以接受任意数量的参数 public XElement (XName name, params object[] content) // XContainer的Add方法也是如此 public void Add (params object[] content)所以当我们创建或添加X-DOM时可以指定任意数量任意类型的子对象(child objects)。那么XContainer为何能接受任意类型的子对象呢要知道原因我们就需要来查看它们在内部实现中是如何处理每个子对象的。下面就是XContainer类型XElement和XDocument的基类型对于子对象的处理方式如果该对象为空忽略它