C# Nut Shell 第十章 LINQ to XML

X-DOM概述

1.XML文档的每一个部分(声明、元素、属性、值和文本)都可以用类来表示,如果使用集合属性来存储子内容,可以用一颗对象树完整地表示整个文档,这就叫文档对象模型(document object model)

2.LINQ to DOM,两部分组成:X-DOM,查询符

3.XDocument是XML树的根节点,封装了根XElement。XElement就表示当前节点,XNode就表示子节点,各种各样的子节点都是一种XNode

4.Load方法从文件、URI、Stream、TextReader以及XmlReader创建X-DOM,Parse从字符串创建X-DOM

5.还可以修改X-DOM并保存会xml文档


实例化 X-DOM

1.手动创建X-DOM

            XElement lastname = new XElement("lastname", "Bloggs");
            lastname.Add(new XComment("nice name"));

            XElement custom = new XElement("Custom");
            custom.Add(new XAttribute("id", 123));
            custom.Add(new XElement("firstname", "zhang"));
            custom.Add(lastname);

2.函数式创建

            XElement custom =
                new XElement("Custom", new XAttribute("id", 123),
                new XElement("firstname", "zhang"),
                new XElement("lastname","bin",
                    new XComment("nice job")));

3.XElement和XDocument都接受一个params的参数,可以支持函数式创建,Add也可以

4.在X-DOM中可以认为所有对象都为有效的对象

5.自动深度克隆,将同一个节点添加到不同的节点之下,会深度拷贝一份,不会共享。


导航和查询

导航到子节点

1.FirstNode、LastNode、Nodes,获得第一个节点,获得最后一个节点,获取所有节点。注意,全部都是直接子节点。获取时,会获取该节点下的所有子节点中的子节点

2.检索元素(XElement),只会获取直接子节点的元素

            var a = from cus in custom.Elements()
                    where cus.Name == "firstname"
                    select cus.Name;

使用Elements()来代替where,相等才能代替

            var b = custom.Elements("firstname");
            var c = custom.Elements().Where(s => s.Name == "firstname");

Elements()方法是可以叠加的,但是当使用2个Elements()时,前一个是实例方法,后一个是扩展方法。

3.Element()会返回单个元素,当元素不存在时,使用a.Element("b")?.Value

4.获取整个树的子元素。Descendants可以获取某个Element或者所有的Element。DescendantNodes可以获取所有的Nodes

导航到父节点

1.所有的XNode都有一个Parent,虚节点一定是一个Element

2.XDocument没有父节点

3.Parent可以找到父元素,也可以多重Parent,如果超出了,会输出null。

4.Ancestors可以找到所有的祖先,也可以输入字符串,找到特定的祖先,父元素被祖元素包含着。AncestorsAndSelf和Ancestors一样,只是还包括自己。在AncestorsAndSelf调用Last可以直接找到根元素。

导航到同级节点

使用PreviousNode和NextNode来遍历节点。XNode内部使用链表来存储的,所以PreviousNode效率很低。

导航至节点的属性

1.属性的父节点就是表示该节点的Element

2.FirstAttribute、LastAttribute、Attribute。。。


更新 X-DOM

简单的值更新

1.SetValue给Value属性赋值,或者通过Value属性赋值。Value属性只能赋值为字符串,SetValue可以赋一个Object。这个赋值操作会替换所有的子节点,就是不管这个节点下有多少元素,只会替换成这一个

更新子节点和属性

1.Add会将一个节点添加到本节点所有子节点的最后面

2.AddFirst会添加到第一个

3.RemoveNodes删除所有子节点,RemoveAttributes删除所有属性,RemoveAll,一次执行上面2条

4.ReplaceNodes替换子节点

5.ReplaceAttributes替换所有属性

6.ReplaceAll替换所有

7.SetElementValue设置某一子节点的值,SetAttributeValue设置属性的值,没有会自动添加

通过父节点更新子节点

AddAfterSelf,AddBeforeSelf,Remove,ReplaceWith都是操作当前节点所在的集合的,所以当前节点必须有父节点,如果没有,则会抛出异常。

1.2个Add方法可以添加节点

2.Remove一处当前节点

3.ReplaceWith可以移除当前节点的同时插入其他内容

4.移除批量元素

通过Elements(xxx)来选取节点集合,通过Elements().Where来选取集合,然后Remove,会把当前子节点下符合要求的节点全部删除。

5.删除某个注释节点

            //删除某个注释节点
            xmlElement.Elements().Where(e=>e.DescendantNodes()
                .OfType()
                .Any(c=>c.Value == "axb"));
            //删除所有注释节点
            xmlElement.DescendantNodes().OfType().Remove();

使用Value

1.设置Value,XML数据有自己的格式,包括字符串、布尔值、日期,当我们为节点赋值时,XML会自动的帮我们用XmlConvert进行转换,保证我们的数据符合XML格式

2.我们输入的数据和XML保存的数据不一定相同,XML不会为我们保存原始数据,所以在类型转换时可能会出错,最好用try/catch

3.在获取属性和节点的值得时候,最好用可空类型接受,这样在节点不存在时也不会报错。还可以用??来去除结果的可空类型

int? a = (int?)xmlElement.Element("fi")?? 3;

4.当节点存在,但是其值为空时,依然会异常。

            //fi存在会异常,fi不存在反而不会异常
            int? a = (int?)xmlElement.Element("fi");

在LINQ中进行类型转换,添加断言(先用Any判断一下)

5.获取Value值会获取该节点包括所有子节点的文本信息,对Value赋值会将所有子节点用XText节点替换

6.直接添加一个字符串,被当做XText节点

xmlElement.Add("hello");

7.如果只想获取节点下的文本,而不获取子节点下的文本,可以采用在节点下使用FirstNode的方法。因为文本其实也会被当做一个Node来对待。如果该节点下多个文本是分别使用XText创建的,那么他们其实属于不同的XText节点,在获取FirstNode时只会被读取第一个,而如果是用字符串形式创建的,则会被当做同一个Node。


文档的声明

XDocument

1.XDocument就是根XElement,也有一些Add,Remove方法,但是是有限的。同时添加了一些额外的元素

2.包含一个XElement对象,一个XDeclaration对象,一个DTD独享,任意的XComment,任意XProcessingInstruction

3.XDeclaration即使没有添加,在doc.save时,在文件中也会添加

4.用XDocument在创建X-DOM时,会创建一个根节点,而XElement不会创建根节点

XML声明

1.XDeclaration有三个对象,版本,编码,standalone,版本永远为"1.0"不管输的是声明,编码使用IETF书写方式

2.ToString输出不会显示声明

3.如果要将XDocument转化成string,同时包含声明,需要用XmlWriter

4.用IO方式来写XML会缺少声明,忽略编码方式


名称和命名空间

XML命名空间

1.如果一个节点定义了一个命名空间,则该节点和它的所有子节点都属于该节点,如果后续的节点不希望继承父节点的命名空间,需要自己指定命名空间,可以指定""

2.指定命名空间的方式

使用xmlns="xxx"的形式,

使用前缀xml,注意使用前缀要同时在其实标签和结束标签都使用


  zhang
  bin

3.属性也能使用命名空间,但是只能使用前缀的方式

在X-DOM中指定命名空间

1.创建NameSpace

            XNamespace ns = "http://zhang.bin.com";
            XName xName = ns + "xname";

2.xml中所有的键值对的key包括节点名的类型都是XName,但是可以用string类型隐式转换

3.namespace可以之间+在XNAME上

X-DOM和默认命名空间

1.在创建X-DOM时,默认回忽略命名空间,应该说会指定默认命名空间为"",也就是说如果父节点指定了命名空间(ns+"xname"),而子节点没指定,则子节点的命名空间为"",

2.在导航X-DOM时,如果节点存在命名空间,而导航时不加,则会表示无法找到

3.可以直接用e.Name=ns + e.Name.LocalName来重新指定命名空间

前缀

1.可以将原来都是通过xmlns输出的命名空间转为前缀

custom.SetAttributeValue(XNamespace.Xmlns + "ns1", ns1);

2.前缀不会影响构建、查询、更新X-DOM的方式,只要按照完整的(包含命名空间)名称即可

3.只有在序列化到文件或者流的时候才需要使用前缀,确保序列化的时候不会出现不必要的命名空间重复

注解

1.注解可以将任何自定义的数据附加在任何XObject上,可以为根目录设置一个注解,也可以为任意一个子节点设置注解。一个节点可以多个注解

2.最好使用嵌套类来做键,因为查找注解是通过类型来查找的。所以很多时候需要用不同的类型来区分不同的注解

将数据映射到X-DOM

            //方法1
            XElement cus = new XElement("custom",
                from c in persons
                select
                 new XElement("Custom", new XAttribute("id", c.Id),
                    new XElement("firstname", c.FirstName),
                    new XElement("lastname", c.LastName)));

            //方法2
            IEnumerable sql = 
                from c in persons
                select
                 new XElement("Custom", new XAttribute("id", c.Id),
                    new XElement("firstname", c.FirstName),
                    new XElement("lastname", c.LastName));
            XElement cus2 = new XElement("Custom", sql);

1.排除空元素

当数据库的数据为空时,要把它设置为NULL,为不是空的Element元素

2.流映射

如果我们的数据映射为X-DOM仅仅为了保存数据(或者调用ToString方法,则可以使用XStreamingElement来替换外层的XElement)。

XStreamingElement只有在Save,ToString或者WriteTo的时候才会加载。但是同时。无法遍历,也无法操作其他很多方法。

Save时会执行多次操作。

3.转换X-DOM

通过查找规律,获取属性或节点的值,构造一个新的X-DOM。

通过查找规律,获取属性或节点的值,对值进行处理,构造一个新的X-DOM。

你可能感兴趣的:(c#,C#,Nut,Shell)