LINQ to XML之Documents、Declarations和Namespaces本篇我们会讨论与XML文档相关的另外几个重要概念:Documents、Declarations和Namesp

📅 2026/7/5 13:41:40
LINQ to XML之Documents、Declarations和Namespaces本篇我们会讨论与XML文档相关的另外几个重要概念:Documents、Declarations和Namesp
我们前面已经说过一个XDocument封装了根元素并且允许我们添加XDeclaration, processing instructions, 文档类型和其他根级类型对象。和W3C DOM不同的是对于LINQ to XML中的X-DOM来讲一个XDocument对象是可选的X-DOM并不需要它来把所有对象联系起来。XDocument提供了和XElement一致的函数式构造器。并且由于XDocument是从Container继承而来所以它也支持AddXXX、RemoveXXX、和ReplaceXXX方法。但和XElement不同的是XDocument可以接受的内容是有限制的它可以接受如下参数一个XElement对象根元素一个XDeclaration对象一个XDocumentType对象任意数量的XProcessingInstruction对象任意数量的XComment对象要创建一个有效的XDocument只有根元素是必须的。XDeclaration是可选的如果它被省略序列化时将会应用默认的设置。一个最简单的XDocument仅有一个根元素var doc new XDocument( new XElement(test, data) );注意我们并没有包含XDeclaration对象但是调用doc.Save方法生成的文件中还是会包含XML declaration信息它会使用默认设置自动生成。下面的示例用来生成一个简单但完全正确的XHTML文件它很好的演示了XDocument可以接受的各种构造参数var styleInstruction new XProcessingInstruction( xml-stylesheet, hrefstyles.css typetext/css); var docType new XDocumentType(html, -//W3C//DTD XHTML 1.0 Strict//EN, http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd, null); XNamespace ns http://www.w3.org/1999/xhtml; var root new XElement(ns html, new XElement(ns head, new XElement(ns title, An XHTML page)), new XElement(ns body, new XElement(ns p, This is the content)) ); var doc new XDocument( new XDeclaration(1.0, utf-8, no), new XComment(Reference a stylesheet), styleInstruction, docType, root); doc.Save(D:\\test.html);test.html文件的最终结果如下?xml version1.0 encodingutf-8 standaloneno? !--Reference a stylesheet-- ?xml-stylesheet hrefstyles.css typetext/css? !DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Strict//EN http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd html xmlnshttp://www.w3.org/1999/xhtml head titleAn XHTML page/title /head body pThis is the content/p /body /htmlXDocument的Root属性可以用来快速的存取其唯一的XElement元素。而XObject的Document属性则可以快速访问所在的XDocument对象X-DOM tree里面的所有对象都继承了该属性Console.WriteLine(doc.Root.Name.LocalName); // html XElement bodyNode doc.Root.Element(ns body); Console.WriteLine(bodyNode.Document doc); // True再次强调一下document对象的子节点并没有Parent对象Console.WriteLine(doc.Root.Parent null); // True foreach (XNode node in doc.Nodes()) Console.Write(node.Parent null); // TrueTrueTrueTrueXML Declarations一个标准的XML文件总是从一个declaration声明开始如下所示?xml version1.0 encodingutf-8 standaloneno?XML declaration的作用是确保文件会被文件读取器正确的读取和理解。XElement和 XDocument会遵循以下规则来处理XML declarations提供一个文件名来调用Save方法时总是写入declaration。提供XmlWriter来调用Save方法时写入declaration除非XmlWriter作了专门的指定。ToString方法从不产生XML declaration。在构造XmlWriter对象时我们可以通过设置OmitXmlDeclaration和ConformanceLevel属性来指示XmlWriter不要产生declaration。XDeclaration对象的存在与否并不影响是否写入XML declaration。换句话说即使没有XDeclaration对象Save方法也会写入默认的declaration相反即使存在XDeclaration对象ToString()方法也不会产生declaration。它的目的是告知XML serialization下面的信息使用什么字符编码text encoding如何设置XML declaration的encoding和standalone属性XDeclaration的构造函数接受3个参数version, encoding, 和standalone属性。下面的例子中test.xml拥有UTF-16编码var doc new XDocument( new XDeclaration(1.0, utf-16, yes), new XElement(test, data) ); doc.Save(test.xml);实际上XML writer会忽略第一个参数version值而总是写入1.0。Names和Namespaces就像.NET类型可以有namespaces命名空间一样XML元素和属性也可以有namespaces。虽然我们在一般的小项目中可能不会用到namespaces但是在产品级的软件中我们就必须使用它来对各种XML文档进行管理了。XML namespaces有两个作用首先和C#的namespaces一样它可以防止命名冲突。在我们把一个XML文件中的数据合并到另外一个XML文件时可能会出现。其次 它赋予一个Name名称以特别的含义。比如名称”nil”可以表示任何东西。但是在http://www.w3.org/2001/xmlschema-instance命名空间中“nil”表示了C#中的null语义。因为XML命名空间非常容易引起混淆我们将先行介绍该主题的普通含义然后再讨论LINQ to XML对他们的使用方式。XML中的Namespaces假设我们希望在CNBlogs.LINQ命名空间中定义一个customer元素。那么有两种方法可以选择。第一种是使用xmlns属性如下所示customer xmlnsCNBlogs.LINQ/Xmlns是一个特殊的保留属性。上例中的xmlns有两个作用它为当前元素指定了一个命名空间。它为所有的后代节点指定了默认的命名空间。这意味着下面的示例中address和postcode会隐式位于CNBlogs.LINQ命名空间之中customer xmlnsCNBlogs.LINQ address postcode02138/postcode /address /customer如果我们希望address和postcode不使用命名空间我们需要明确指定如下所示customer xmlnsCNBlogs.LINQ address xmlns postcode02138/postcode!-- postcode 现在继承空的命名空间 -- /address /customer前缀Prefix指定命名空间的另一种方式是使用前缀。前缀是我们为命名空间指定的一个别名目的是为了节省输入时间。使用前缀分两步定义前缀和使用前缀。我们可以按如下方式同时完成这两步操作nut:customer xmlns:nutCNBlogs.LINQ/在上面的代码中会发生两件特别的事情。后面的xmlns:nut...定义了一个名为nut的前缀这样该元素和所有的后代节点都可以使用该前缀了。前面的nut:customer把新创建的前缀赋给了customer元素。和xmlns不同的是带前缀的元素并没有为后代节点指定默认的命名空间即前缀只作用于当前元素。下面的XML中firstname的命名空间为空nut:customer xmlns:nutCNBlogs.LINQ firstnameJoe/firstname /customer如要为firstname也指定CNBlogs.LINQ命名空间我们得进行如下改写nut:customer xmlns:nutOReilly.Nutshell.CSharp nut:firstnameJoe/firstname /customer当然我们也可以为了后代节点定义一个或多个前缀而不必在当前节点中使用它。下面的XML定义了两个前缀i和z但当前的customer元素命名空间保持为空customer xmlns:ihttp://www.w3.org/2001/XMLSchema-instance xmlns:zhttp://schemas.microsoft.com/2003/10/Serialization/ ... /customer如果customer是根节点那么整个document都将拥有i和z命名空间。当我们需要从多个命名空间中获取元素时使用前缀就会非常方便我们可以为每个命名空间定义一个前缀。在X-DOM中指定Namespaces到目前为止我们只为XElement和XAttribute使用了简单的名字一个简单的字符串。它表示该XML name位于空的命名空间中就像.NET全局命名空间中的类型一样。有数种方式可以在X-DOM中指定命名空间。第一种是在名称之前的大括号中指定如下所示var e new XElement ({http://www.cnblogs.com/xmlspace}customer, LifePoem); Console.WriteLine (e.ToString()); //产生的XML如下: customer xmlnshttp://www.cnblogs.com/xmlspaceLifePoem/customer第二张更有效率的方式是使用XNamespace和XName类型。下面是他们的定义public sealed class XNamespace { public string NamespaceName { get; } } public sealed class XName // 一个本地名称和一个可选的namespace { public string LocalName { get; } public XNamespace Namespace { get; } // 可选 }这两个类型都定义了来自string的隐式转换所以下面的代码是正确的XNamespace ns http://www.cnblogs.com/xmlspace; XName localName customer; XName fullName {http://www.cnblogs.com/xmlspace}customer;XName还重载了运算符这允许我们不使用大括号来组合namespace和nameXNamespace ns http://www.cnblogs.com/xmlspace; XName fullName ns customer; Console.WriteLine(fullName); // {http://www.cnblogs.com/xmlspace}customer实际上X-DOM中所有接受元素或属性名称的的构造函数和方法其参数类型为XName而不是string。在我们前面的例子中可以使用string的原因是隐式转换。不管是元素还是属性指定命名空间的方式都是一样的XNamespace ns http://domain.com/xmlspace; var data new XElement(ns data, new XAttribute(ns id, 123) );X-DOM和默认命名空间在X-DOM的创建过程中它会忽略默认命名空间的概念直到真正输出XML时才会使用默认命名空间。这意味着我们在创建子元素时我们必须显示指定必须的命名空间它不会从父节点继承XNamespace ns http://www.cnblogs.com/xmlspace; var data new XElement(ns data, new XElement(ns customer, Bloggs), new XElement(ns purchase, Bicycle) ); Console.WriteLine(data.ToString());但是当X-DOM读取或输出XML时它会应用默认的命名空间所以上面的代码输出如下data xmlnshttp://www.cnblogs.com/xmlspace customerBloggs/customer purchaseBicycle/purchase /data下面的代码Console.WriteLine (data.Element (ns customer).ToString()); // 输出单个元素时会加上默认的命名空间 customer xmlnshttp://www.cnblogs.com/xmlspaceBloggs/customer如果我们在创建子XElement时没有指定命名空间那么他们的命名空间为空如下所示XNamespace ns http://www.cnblogs.com/xmlspace; var data new XElement(ns data, new XElement(customer, Bloggs), new XElement(purchase, Bicycle) ); Console.WriteLine(data.ToString());