C# Notizen 14 XML

经常需要处理文本文件,命名空间 System.IO 提供的类使得这种任务更容易,但是使用这些类不能处理和操作 XML 格式的结构化文本。XML 指的是可扩展标记语言(Extensible Markup Language),这是一种简单而灵活的文本格式,使用它可以独立于平台的方式交换数据。
将XML用作数据交换格式很常见,不仅.NET Framework这样做,其他Microsoft产品也如此。.NET Framework 将其用于通过 SOAP 提供的 Web 服务和 WCF(Windows Communication Foundation),将其用作WPF(Windows Presentation Foundation)和XAML (Silverlight Extensible Application Markup Language,Silverlight可扩展应用程序标记语言)文件的格式,将其用作Windows WF(Workflow Foundation)文件的格式;它还是ADO.NET的组成部分。
虽然 XML 是基于文本的,并且对人来说很容易理解,但是必须能够通过编程操作XML。这是使用XML分析器完成的。.NET Framework提供了两个XML分析器:一个是基于流的,可即时读取XML流;另一个是基于树的,它必须将整个流读入内存后才能构建树。

一、XML DOM
要以编程方式读取和操作XML文档,就必须在内存中使用XML文档对象模型(Document Object Model,DOM)表示它。DOM提供了一种在内存中表示XML数据的结构化方式,常用于将XML数据读入内存,以调整其结构、添加或删除元素以及修改元素包含的数据。
举个栗子:

    <books>
        <book>
        <title>note</title>
        <isbn-10>010</isbn-10>
        <author>zz</author>
        <price currency="EU">250.00</price>
        <publisher>
            <name>xml</name>
            <state>china</state>
        </publisher>
        </book>
    </books>

二、LiNQ to XML
LINQ to XML通过LINQ扩展方法暴露了XML DOM,能够操作和查询已加载到内存中的XML文档。使用LINQ to XML创建和操作XML文档时,需要的所有类都包含在命名空间System.Xml.Linq中。
下图列出了最常用的类:

Paste_Image.png

2.1 XDocument
XDocument 类表示 XML 文档实例。除非需要指定文档类型声明、处理指令(供 XML分析器使用)或顶级注释,否则很少使用XDocument实例,而应使用XElement类。

2.2 XElement和XAttribute
XElement类表示XML元素,是最常用的类之一,它提供了很多很有用的方法和属性,可用于创建、修改和查询XML数据。XML属性(attribute)是与XML元素相关联的名称-值对,用 XAttribute 类表示。不同于元素,属性不是 XML 树中的节点。由于属性是与元素相关联的名称-值对,因此其名称在相应的元素中必须是唯一的。
XElement实例包含一系列元素的属性。在XAttribute类中,最常用的属性是NextAttribute和PreviousAttribute,它们对浏览元素的属性序列很有帮助。

如下示例使用LINQ to XML创建XML文档

XElement document = new XElement("books",    
    new XElement("book",        
        new XElement("title","note"),        
        new XElement("isbn-10","010"),        
        new XElement("author","zz"),        
        new XElement("price", new XAttribute("currency", "EU"),250),        
        new XElement("publisher",            
            new XElement("name","xml"),            
            new XElement("state","china"))));

ps:SetElementValue和SetAttributeValue
创建XML时,并非只能使用如上所示的构造函数语法,也可使用XElement类提供的方法SetElementValue和SetAttributeValue。
这些方法让您能够使用名称-值对设置元素和属性,从而添加、修改或删除它们。如果指定的元素或属性名不存在,就创建它;否则,将其值修改为指定的值。如果值为null,就删除指定的元素或属性。修改或删除时,将修改或删除第一个与指定名称匹配的元素或属性。

如下示例与上一个示例创建的 XML 相同,但使用的是方法SetElementValue和SetAttributeValue:

XElement document = new XElement("books",    
    new XElement("book",        
        new XElement("publisher")));        

        bookElement.SetElementValue("title","note");        
        bookElement.SetElementValue("isbn-10","010");        
        bookElement.SetElementValue("author","zz");        
        bookElement.SetElementValue("price", 250);        
        bookElement.Element("price").SetAttributeValue("currency", "EU");        
        bookElement.SetElementValue("name","xml");        
        bookElement.SetElementValue("state","china”);

ps:XML字符编码
对于包含非法XML字符的文本,XElement和XAttribute类自动处理其编码和解码。对于下面的语句:

XElement comments = new XElement("comments",
"This line contains special characters <node> & </node>");

将自动编码成如下语句:

<comments>This line contains special characters <node>
&
</node></comments>

检索值时,将自动进行解码

2.3 XName和XNamespace
XML名称指的是XML文档中元素或属性的名称,由两部分组成:XML命名空间和本地名称。XML命名空间能够确保元素和属性的名称是唯一的,以免XML文档的不同部分发生冲突。声明XML命名空间后,可选择仅在该命名空间内唯一的本地名称。
使用XML命名空间时,可利用XML前缀来创建XML命名空间的缩写。虽然XML前缀提高了XML文档的可读性,但是增加了复杂度,因为其含义依赖于上下文。.NET Framework提供了Xnamespace,用于表示XML命名空间。
XName类表示本地名称。在LINQ to XML中,每当需要XML名称时,都使用XName。所幸的是,XName提供了将string隐式转换为XName的功能,因此很少直接使用XName。每个XName都包含一个XNamespace,如果元素不在任何命名空间内,那么其XNamespace为XNamespace.none。

ps:XML命名空间
声明XML命名空间的语法与声明XML属性的语法相同,因此它们常被视为属性,虽然它们不是属性。在XML树中,LINQ to XML将命名空间表示为属性,以简化编程接口。要判断属性实际上是不是命名空间声明,可使用IsNamespaceDeclaration属性。

如下示例使用LINQ to XML创建包含命名空间的XML:

XNamespace ns = "http://www.w3.org/1999/xhtml";
XElement document = new XElement(ns + "books",    
    new XElement(ns + "book",    
    new XElement(ns + "title","note"),    
    new XElement(ns + "isbn-10","010"),    
    new XElement(ns + "author","zz"),    
    new XElement(ns + "price",new XAttribute("currency", "EU") 250),    
    new XElement(ns + "publisher",        
        new XElement(ns + "name","xml"),        
        new XElement(ns + "state","china"))));

这将生成如下一个示例所示的XML,
包含命名空间的XML文档:

<books xmlns="http://www.w3.org/1999/xhtml">    
    <book>        
        <title>note</title>        
        <isbn-10>010</isbn-10>
        <author>zz</author>
        <price currency="EU">250.00</price>
        <publisher>
            <name>xml</name>
            <state>china</state>
        </publisher>
    </book>
</books>

虽然LINQ to XML类自动处理命名空间声明,但是有时需要提供命名空间前缀,以控制如何在 XML 数据中表示命名空间。为此,可显式地指定表示命名空间的前缀,方法是添加一个xmlns属性,如下所示:

XNamespace ns = "http://www.w3.org/1999/xhtml";
XElement document = new XElement(ns + "books",
    new XElement(ns + "book",    new XElement(ns + "title","note"),
    new XElement(ns + "isbn-10","010"),
    new XElement(ns + "author","zz"),
    new XElement(ns + "price",new XAttribute("currency", "EU") 250),
    new XElement(ns + "publisher",
        new XElement(ns + "name","xml"),
        new XElement(ns + "state","china"))));

生成如下示例的XML:

<ns:books xmlns:ns="http://www.w3.org/1999/xhtml">
    <ns:book>
        <ns:title>note</ns:title>
        <ns:isbn-10>010</ns:isbn-10>
        <ns:author>zz</ns:author>
        <ns:price currency="EU">250.00</ns:price>
        <ns:publisher>
            <ns:name>xml</ns:name>
            <ns:state>china</ns:state>
        </ns:publisher>
    </ns:book>
</ns:books>

处理使用命名空间的文档时,通常通过URI而不是命名空间前缀来引用命名空间。这让您能够使用全限定名(也叫扩展名称,expanded name),其格式如下:
{namespacename}name

ps:原子化
XNamespace对象被原子化(atomized),这意味着如果两个对象的URI相同,它们就将共享一个实例。虽然创建XElement或XAttribute实例时可以使用扩展名称,但是这样做可能影响性能。每当遇到包含扩展名称的字符串时,都必须对其进行分析,以找出原子化的命名空间和名称。

三、选择和查询XML
通过XElement实例将XML文档载入内存后,几乎总是需要选择或查询信息。从XNode派生而来的所有类都提供了这样的方法和属性,即可用于直接导航到XML树中特定的节点。
属性 FirstNode 和 LastNode 分别返回第一个和最后一个子节点;属性 NextNode 和PreviousNode让您能够在节点集合中向前和向后移动;而属性Parent让您能够直接导航到父节点。

如下示例演示了使用XElement的导航属性:

XElement document = new XElement("books",
    new XElement("title","note");
    new XElement("isbn-10","010");
    new XElement("author","zz");
    new XElement("price",new XAttribute("currency", "EU"), 250));

Console.WriteLine(document.LastNode);
Console.WriteLine(document.FirstNode);
Console.WriteLine(document.LastNode.Parent);
Console.WriteLine(document.LastNode.PreviousNode);
Console.WriteLine(document.FirstNode.NextNode);

XElement还提供了属性FirstAttribute和LastAttribute,它们分别返回元素的第一个和最后一个属性。如果元素没有属性,它们就都返回 null;如果元素只有一个属性,它们返回的值就相同。检索第一个或最后一个属性后,可使用NextAttribute和PreviousAttribute在属性集合中向前和向后移动。
虽然这些属性很方便,但是没有提供太高的灵活性。如果将XML树中的每个节点都视为一个节点序列,就可使用LINQ查询,这是因为节点集合是一个IEnumerable<T>实例。
如下程序演示了如何对上述示例创建的XElement执行简单的LINQ查询:

foreach (var o in document.Elements().Where(e => (string)e.Element("author") == "zz"))
{
    Console.WriteLine(o);
}

如果没有指定名称,就将返回所有的子元素;否则,只返回该名称指定的子元素。

ps:LINQ to XML查询和XPath查询
如果使用命名空间 System.XML中的传统XML DOM类,就必须使用XPath查询来选择节点集合或单个节点。在LINQ to XML中,不再需要这样做,而由命名空间System.Xml.XPath中的一组扩展方法提供查询支持。
扩展方法如下:

方法 描述
CreateNavigator 创建一个对应于XNode的XPathNavigator。
XPathEvaluate 计算XPath表达式,返回一个包含表达式结果的对象。
XPathSelectElement 使用XPath表达式选择XElement。
XPathSelectElements 使用XPath表达式选择一个元素集合。

给方法Where传递了一个Lambda表达式,它使得结果序列只包含这样的元素:包含子元素author,且该子元素的值为字符串Dorman。方法Element返回第一个与指定名称匹配的XElement。
选择属性也一样容易,但是使用方法Attributes和Attribute。方法Attribute返回一个属性,其名称与指定的名称相同。如果没有匹配的属性,就返回 null。方法 Attributes 返回一个IEnumerable<XAttribute>,其中包含当前XElement的属性。将名称作为参数传递给Attributes时,如果没有找到匹配的属性,就返回一个空集合;否则,将返回包含一个属性的集合,因为在每个元素中,属性的名称必须是唯一的。
四、修改XML
虽然创建和选择XML很重要,但是修改XML也同样重要。通过使用XNode及其派生类提供的方法,这种任务很容易完成。修改 XML 时,导航到目标节点的技术将影响修改发生的时间。如果使用前一节开头介绍的属性(如FirstNode或LastNode),修改将在调用方法时进行。删除或替换一个节点,修改结果就将立刻在内存中的 XML 树中反映出来。如果使用的是 XML 查询,调用的修改方法就将在查询结果被遍历时执行,这与 LINQ 查询的默认行为(推迟执行)一致。可使用方法SetElementValue和SetAttributeValue来添加元素或属性、删除元素或属性以及修改元素或属性的值。还可使用方法SetValue来修改当前元素或属性的值,如下程序使用了SetValue来修改元素price的内容:

XElement books = XElement.Load("books.xml");
XElement book = books.Elements("book").FirstOrDefault(b => (string)b.Element("author") == "zz");
book.Element("price").SetValue(25);

替换数据也很简单,但是使用方法 ReplaceAll、ReplaceAttributes、ReplaceNodes 或ReplaceWith。方法 ReplaceAll 替换当前元素的所有子节点和属性,而方法 ReplaceAttributes和ReplaceNodes分别替换所有属性和所有子节点。

ps:ReplaceWith和子节点
ReplaceWith 只用指定的元素替换当前元素。如果要替换的元素有子节点,就不会自动将这些子节点包含在新元素中。
ReplaceWith只用指定的元素替换当前元素。如下程序使用ReplaceWith将元素price替换为新元素:

XElement books = XElement.Load("books.xml");
XElement book = books.Elements("book").FirstOrDefault(b => (string)b.Element("author") == "zz");
book.Element("price").ReplaceWith(25);

要删除当前元素或属性,可使用方法 Remove;要删除当前元素的所有属性,可使用方法 RemoveAttributes;要删除所有的子节点,可使用 RemoveNodes;要删除当前元素的所有子节点和属性,可使用方法RemoveAll。如下所示:

XElement books = XElement.Load("books.xml");
books.Elements("book").FirstOrDefault(b => (string)b.Element("author") == "zz").Remove();

最后,要添加新元素,可使用方法 Add、AddAfterSelf、AddBeforeSelf 或 AddFirst。方法Add将指定的内容作为子节点加入到当前元素中,而AddFirst将指定的内容作为第一个子节点。方法AddAfterSelf和AddBeforeSelf分别将指定的内容作为兄弟节点加入到当前节点后面和前面。如下所示:

XElement books = XElement.Load("books.xml");
XElement book = books.Elements("book").FirstOrDefault(b => (string)b.Element("author") == "zz");
book.Add(new XElement("season","winter”));
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 1. XML简介 以下内容来自于http://www.w3school.com.cn/xml 基本知识 XML 和...
    WebSSO阅读 1,867评论 1 7
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,678评论 6 342
  • 2013年,更新了 犯规后交换击球权 的规则: 与击球不相关的犯规将判罚为 Penalty,罚7分,不交换击球权 ...
    Agreal阅读 455评论 0 1
  • 我坐在榕树下看着一场绵绵阴雨,雨下了许久。忽然想起在我的人生中某个时段,曾有个少年与他的父亲一起的某个片段,那片段...
    色哥倾慕阅读 202评论 0 0