代码改变世界

XDocument简单入门

2013-03-05 17:10  糯米粥  阅读(12650)  评论(14编辑  收藏  举报
1、什么是XML?
2、XDocument和XmlDocument的区别?
3、XDocument
4、XmlDocument
5、LINQ to XML
6、XML序列化与反序列化

因为这几天用到了不熟悉的xml统计数据,啃了网上的资料解决了问题,故总结下xml知识。

什么是XML?
    XML(extensible markup language)可扩展标记语言,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。
  特性:
    XML要求所有的标记必须成对出现;HTML标记不区分大小写,XML则大小敏感,即区分大小写。
  语法:
  1 任何的起始标签都必须有一个结束标签。
  2 可以采用另一种简化语法,可以在一个标签中同时表示起始和结束标签。这种语法是在大于符号之前紧跟一个斜线(/),例如<tag/ >。XML解析器会将其翻译成<tag></tag>。
   3 标签必须按合适的顺序进行嵌套,所以结束标签必须按镜像顺序匹配起始标签,例如this is a samplestring。这好比是将起始和结束标签看作是数学中的左右括号:在没有关闭所有的内部括号之前,是不能关闭外面的括号的。
   4 所有的特性都必须有值。可以是空值
   5 所有的特性都必须在值的周围加上双引号。
 
XDocument和XmlDocument的区别
  XDocument和XmlDocument都可以用来操作XML文档,XDocument是.net 3.5为Linq to XML准备的轻量级Document对象,在功能上他和XmlDocument差不多,但是Linq to XML只能配合XDocument使用
 
XDocument
先看一段简单的xml代码,一步一步来揭开xml的面纱
            // 创建一个xml文档  XDocument所属命名空间:using System.Xml.Linq;
            XDocument xDoc = new XDocument();
            // 添加根节点
            XElement xRoot = new XElement("Root");
            // 添加节点使用Add
            xDoc.Add(xRoot);

            // 创建一个学生加到root中
            // 1、创建学生节点
            XElement xStudent = new XElement("Student");

            // 2、创建学生姓名、年龄、性别
            XElement xName = new XElement("Name");
            XElement xAge = new XElement("Age");
            XElement xGender = new XElement("Gender");

            //给每个元素赋值
            xName.Value = "张三";
            xAge.Value = "19";
            xGender.Value = "";

            // 3、添加节点(没有顺序之分)
            xStudent.Add(xName, xAge, xGender);  //把学生姓名,年龄,性别元素添加到学生节点下
            xRoot.Add(xStudent);    //把学生节点添加到根节点下

            // 为Student添加属性
            XAttribute xId = new XAttribute("id", ".Net01");
            xStudent.Add(xId);

            // 保存该文档  
            xDoc.Save("myxml.xml");

运行后的结果:

<?xml version="1.0" encoding="utf-8"?>
<Root>
  <Student id=".Net01">
    <Name>张三</Name>
    <Age>19</Age>
    <Gender></Gender>
  </Student>
</Root>

但XDocument提供了更舒服的创建xml方式:

  我们先看一段XDocument代码(这里推荐几种常见的方法)这是后面要介绍的linq to xml 方式
  以下三个列子都是创建一个xml文档。当然。你可以根据自己的需要选择更好的方式
 
示列一:
  static void saveXml2()
        {
            //如果你喜欢这样写的话,那就一级一级阶梯状排列好。很有层次感,看起来特明了
            XDocument xDoc = new XDocument(
               new XElement("Root",
                    new XElement("FlyInfo",
                        new XElement("Sum",
                            new XElement("AirLine", "航空"),
                            new XElement("Seat", "经济舱"),
                            new XElement("Rating", "A"),
                            new XElement("Gai", "可以改"),
                            new XElement("Tui", "可以退"),
                            new XElement("Qian", "可以签"),
                            new XElement("com",
                                new XElement("comm", "暂无")
                            )
                        )
                    ),

                    new XElement("FlyInfo",
                        new XElement("Sum",
                            new XElement("AirLine", "航空"),
                            new XElement("Seat", "头等舱"),
                            new XElement("Rating", "R"),
                            new XElement("Gai", "不可以改"),
                            new XElement("Tui", "不可以退"),
                            new XElement("Qian", "不可以签")
                        )
                    )
                )
            );

            xDoc.Save("Test.xml"); 
        }

运行成功后就生成了一个Test.xml文档

 

示列二:

  也许你也见过这样的写法,效果是相同的(与上面树结构不同)

   static void saveXMLs()
        {
            XDocument xDoc = new XDocument();  //实例化一个xml(内容)文档
            XElement xRoot = new XElement("Root");  //创建一个xml根节点
            xDoc.Add(xRoot);  //把根节点添加到xml文档  记住:一个xml文档只能有一个根节点,可以多个父节点。多个子节点,可以把任何一个元素作为父节点或子节点

            //以下均是xml元素  FlyInfo元素(父节点)下又有子元素(子节点)  如果把一个学校(根节点) 很多个教室(父节点)  每个班级的学生(子节点)就是所属班级(父节点)的子节点
            XElement xFlyInfo = new XElement("FlyInfo");  //
            XElement xAirLine = new XElement("AirLine");
            XElement xSeat = new XElement("Seat");
            XElement xRating = new XElement("Rating");
            XElement xGai = new XElement("Gai");
            XElement xTui = new XElement("Tui");
            XElement xQian = new XElement("Qian");

            xAirLine.Value = "航空";
            xSeat.Value = "经济舱";
            xRating.Value = "A";
            xGai.Value = "可以改";
            xTui.Value = "可以退";
            xQian.Value = "可以签";


            xRoot.Add(xAirLine, xSeat, xRating, xGai, xTui, xQian);  //把元素添加到根节点中

            xDoc.Save("test.xml"); //保存xml文档
        }

 

运行结果:

 

示列三:

  当然你也可以用DataSet,先保存在内存缓存中,然后在保存到磁盘

   static void saveDtable()
        {
            DataSet ds = new DataSet("Root"); //实例化一个DataSet 并初始化值为Root,映射到xml时则是根节点,当没初始化值时。默认是NewDataSet  

            DataTable table = new DataTable("FlyInfo"); //实例化一个table。同时取名为FlyInfo。当映射到xml文档时则是xml文档中的一个父节点,table必须指定表名,因为它可没有默认值。
            //table.TableName = "FlyInfo";  //如果初始化没设置表名,可以通过属性设置。也OK

            //给Table添加列,映射到xml文档时是当前父节点的子节点
            table.Columns.Add("AirLine");
            table.Columns.Add("Seat");
            table.Columns.Add("Rating");
            table.Columns.Add("Gai");
            table.Columns.Add("Tui");
            table.Columns.Add("Qian");

            //创建与该表具有相同架构的新行
            DataRow dr = table.NewRow();

            //添加数据
            dr["AirLine"] = "航空";
            dr["Seat"] = "经济舱";
            dr["Rating"] = "A";
            dr["Gai"] = "可以改";
            dr["Tui"] = "可以退";
            dr["Qian"] = "可以签";

            table.Rows.Add(dr); //把每一行添加到table

            //以下两句效果相同
            ds.Tables.Add(table); //把table添加到DataSet(数据集)中
            //ds.Tables.Add(table.Copy()); //这样也行,复制当前表格的数据和结构,然后添加到DataSet中

            ds.WriteXml("tableDemo.xml");  //保存咯

            //下面都是清除数据,释放资源 
//dt.Rows[0].Delete(); 删除某一行
            //dt.AcceptChanges(); 返回删除后的数据
ds.Clear(); ds.Tables.Clear(); }

 

嗯。以上几个示列都是创建新的xml文档,现在我们来看看如何给已有的xml文档添加新的数据。还是拿上面xml文档例子来进行。

已有的xml数据:

<?xml version="1.0" standalone="yes"?>
<Root>
  <FlyInfo>
    <AirLine>航空</AirLine>
    <Seat>经济舱</Seat>
    <Rating>A</Rating>
    <Gai>可以改</Gai>
    <Tui>可以退</Tui>
    <Qian>可以签</Qian>
  </FlyInfo>
</Root>

 

接下来我们一步一步来看,当完成一个就同时看运行后的代码。比较明了

来看怎么给节点末尾添加新的节点:

       //XDocument提供了Load()静态方法
            XDocument xDoc = XDocument.Load("tableDemo.xml"); //加载xml文档。这里是相对路径
      //当你生了个儿子。想上户口簿时,就给其父亲加个儿子节点
            XElement xfa = xDoc.Root.Element("FlyInfo");  //找到父亲(FlyInfo是父节点,所属他下面的都是子节点)
            XNode xson = xfa.LastNode; //找到最后一个儿子节点

            //这里给父亲添加个儿子(在末尾添加的)
            xson.AddAfterSelf(
                 new XElement("son","还没生子") //这里son没有儿子。也就是son节点没有子节点
                );
      xDoc.Save("tableDemo.xml"); //保存数据  其实总的来说是把全部数据读到内存中然后在内存中追加数据后再全部保存tableDemo.xml

看运行后的结果

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Root>
  <FlyInfo>
    <AirLine>航空</AirLine>
    <Seat>经济舱</Seat>
    <Rating>A</Rating>
    <Gai>可以改</Gai>
    <Tui>可以退</Tui>
    <Qian>可以签</Qian>
    <son>还没生子</son>
  </FlyInfo>
</Root>

 

你运行一次就会增加一个"儿子"挺划算的······

当然,也许你会想,那如果我要在指定的儿子节点后面添加一个节点(这样是指兄弟节点)呢?

  1、根据元素名(节点名);   

  2、根据元素属性来定位某个节点;

 

先看第一种情况:根据元素名定位节点 

  如果你是顺序看下来的。那你看到这句代码就知道 XElement xfa = xDoc.Root.Element("FlyInfo"); 

  这就是根据元素名来找到“FlyInfo”节点,这是在文档开始顺序查找的。也就是说不管有多个“FlyInfo”节点。程序也只会找到第一个。并停止,

  当然如果没找到“FlyInfo”节点,显然这句:“XNode xson = xfa.LastNode; //找到最后一个儿子节点”就会报异常:未将对象引用设置到对象的实例-点击看此博文能学到更多。因为xfa变量是空怎么会有子节点呢?

好。我们在节点“<Seat>经济舱</Seat>”后添加一个兄弟节点“<Info>详细信息</Info>”

只要改变查找方式: XElement i = xDoc.Root.Element("FlyInfo").Element("Seat"); //父节点(FlyInfo)下的子节点(Seat)。

 其他代码不变:

XElement i = xDoc.Root.Element("FlyInfo").Element("Seat"); //父节点(FlyInfo)下的子节点(Seat)

            //这里给父亲添加个儿子(在末尾添加的) 在之前添加用:AddBeforeSelf
            i.AddAfterSelf(
                 new XElement("Info","详细信息") //这里Info没有儿子。也就是Info节点没有子节点
                );

            xDoc.Save("tableDemo.xml");

 

运行后看结果:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Root>
  <FlyInfo>
    <AirLine>航空</AirLine>
    <Seat>经济舱</Seat>
    <Info>详细信息</Info>
    <Rating>A</Rating>
    <Gai>可以改</Gai>
    <Tui>可以退</Tui>
    <Qian>可以签</Qian>
  </FlyInfo>
</Root>

 

 

然后第二种情况:根据元素属性定位节点 

既然是元素属性,那元素就必然会有相应的属性名,其实,当你创建xml数据的时候就可以初始化给Element(元素)添加相应的属性;

我这里初始化没有添加,那现在我们来给“Rating”元素添加一个属性并赋值:name="mark",添加属性用XAttribute类

XElement i = xDoc.Root.Element("FlyInfo").Element("Rating"); //父节点(FlyInfo)下的子节点(Rating)

            i.Add(new XAttribute("name", "mark"));
 xDoc.Save("tableDemo.xml");

 

同样运行看结果:“Rating ”元素添加了个name属性

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Root>
  <FlyInfo>
    <AirLine>航空</AirLine>
    <Seat>经济舱</Seat>
    <Rating name="mark">A</Rating>
    <Gai>可以改</Gai>
    <Tui>可以退</Tui>
    <Qian>可以签</Qian>
  </FlyInfo>
</Root>

 

 属性有了。那么就可以找到有name属性的元素

IEnumerable<XNode> atr = xDoc.Root.Element("FlyInfo").Nodes();  //找到FlyInfo节点下所有子节点

            foreach (XElement item in atr)  //遍历节点
            {
                if (item.Attribute("name") != null) //不为null说明找到了有name属性的节点,拆开写的话: XAttribute at = item.Attribute("name"); 然后if(at!=null){...}
                {
                    item.AddAfterSelf(
                            new XElement("attr","根据属性查找节点并添加")
                        );
                }
            }
            xDoc.Save("tableDemo.xml");

 

运行后看结果:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Root>
  <FlyInfo>
    <AirLine>航空</AirLine>
    <Seat>经济舱</Seat>
    <Rating name="mark">A</Rating>
    <attr>根据属性查找节点并添加</attr>
    <Gai>可以改</Gai>
    <Tui>可以退</Tui>
    <Qian>可以签</Qian>
  </FlyInfo>
</Root>

 

XmlDocument
 
  接下来我们我们看看XmlDocument,它出现在XDocument之前,你可以看看有哪些异同。
 //创建一个xml文档
            XmlDocument xmlDoc = new XmlDocument();

            //定义xml声明,XmlDoxument跟XDocument不同,默认没有声明,
            XmlDeclaration xmlDec = xmlDoc.CreateXmlDeclaration("1.0", "GB2312", "yes");
            xmlDoc.AppendChild(xmlDec);

            //创建一个根节点
            XmlElement xmlRoot = xmlDoc.CreateElement("Root");

            //把根节点添加到xml文档
            xmlDoc.AppendChild(xmlRoot);

            //创建学生节点
            XmlElement xmlStudent = xmlDoc.CreateElement("Student");

            //创建学生姓名,年龄,性别
            XmlElement xmlName = xmlDoc.CreateElement("Name");
            XmlElement xmlAge = xmlDoc.CreateElement("Age");
            XmlElement xmlGender = xmlDoc.CreateElement("Gender");

            //赋值
            xmlName.InnerText = "张三";
            xmlAge.InnerText = "20";
            xmlGender.InnerText = "";

            //当然。你喜欢的话,可以添加属性
            XmlAttribute xId = xmlDoc.CreateAttribute("id");
            xId.Value = ".Net01";
            xmlStudent.SetAttributeNode(xId);

            //添加节点
            xmlStudent.AppendChild(xmlName);
            xmlStudent.AppendChild(xmlAge);
            xmlStudent.AppendChild(xmlGender);

            xmlRoot.AppendChild(xmlStudent);

            //保存xml文档
            xmlDoc.Save("xmlDocument.xml");

同样,运行后是我们期待的结果:

<?xml version="1.0" encoding="GB2312" standalone="yes"?>
<Root>
  <Student id=".Net01">
    <Name>张三</Name>
    <Age>20</Age>
    <Gender></Gender>
  </Student>
</Root>

 

 那同样我们用XmlDocument来给已有的xml文档添加数据,还是用“FlyInfo”这个例子

首先来看已有的xml数据

<?xml version="1.0" encoding="utf-8"?>
<Root>
  <FlyInfo>
    <AirLine>航空</AirLine>
    <Seat>经济舱</Seat>
    <Rating>A</Rating>
    <Gai>可以改</Gai>
    <Tui>可以退</Tui>
    <Qian>可以签</Qian>
  </FlyInfo>
</Root>

当你添加一个与“FlyInfo”有相同结构的节点时:

static void xmlDocu()
        {
            //实例化一个XmlDocument文档
            XmlDocument xmlDoc = new XmlDocument();
            //加载xml文档
            xmlDoc.Load("xmlDoumer.xml");

            //ChildNodes[0]这是找到第一个,树结构一样。找谁都行。当然要保证有咯
            XmlNode node = xmlDoc.DocumentElement.ChildNodes[0].CloneNode(true);  //因为是添加具有相同节点树结构,所有找到一个树结构,创建副本,即克隆

            //以下是获取父节点下的子节点  这里更改了每个子元素的内容。如果你不更改。默认值还是原来的值
            node["AirLine"].InnerText = "北京航空";
            node["Seat"].InnerText = "头等舱";
            node["Rating"].InnerText = "AB";
            node["Gai"].InnerText = "不改";
            node["Tui"].InnerText = "不退";
            node["Qian"].InnerText = "不签";
           
            //在根节点下最后一个子元素末尾添加
            xmlDoc.DocumentElement.AppendChild(node);
            xmlDoc.Save("xmlDoumer.xml"); //保存
        }

运行结果:

<?xml version="1.0" encoding="utf-8"?>
<Root>
  <FlyInfo>
    <AirLine>航空</AirLine>
    <Seat>经济舱</Seat>
    <Rating>A</Rating>
    <Gai>可以改</Gai>
    <Tui>可以退</Tui>
    <Qian>可以签</Qian>
  </FlyInfo>
  <FlyInfo>
    <AirLine>北京航空</AirLine>
    <Seat>头等舱</Seat>
    <Rating>AB</Rating>
    <Gai>不改</Gai>
    <Tui>不退</Tui>
    <Qian>不签</Qian>
  </FlyInfo>
</Root>

也许你会说这是具有相同xml树结构。如果我想添加自己的xml树结构呢?那......

static void xmlDocu()
        {
            //实例化一个XmlDocument文档
            XmlDocument xmlDoc = new XmlDocument();
            //加载xml文档
            xmlDoc.Load("xmlDoumer.xml");

            //ChildNodes[0]这是找到第一个,树结构一样。找谁都行。当然要保证有咯
            XmlNode node = xmlDoc.DocumentElement.ChildNodes[0].CloneNode(true);  //因为是添加具有相同节点树结构,所有找到一个树结构,创建副本,即克隆

            //以下是获取父节点下的子节点  这里更改了每个子元素的内容。如果你不更改。默认值还是原来的值
            node["AirLine"].InnerText = "北京航空";
            node["Seat"].InnerText = "头等舱";
            node["Rating"].InnerText = "AB";
            node["Gai"].InnerText = "不改";
            node["Tui"].InnerText = "不退";
            node["Qian"].InnerText = "不签";
           
  //修改属性:很显然要保证子节点有属性。同样 (当然这也是你百分百确定有这个属性)
            //node["AirLine"].Attributes["id"].Value = "90";
            //当然,如果你不想要这个元素 就干掉
            //node.RemoveChild(node["Qian"]);

 //创建一个子节点的两种方式
            XmlElement addNode = xmlDoc.CreateElement("Phone");
            XmlNode xno = xmlDoc.CreateNode(XmlNodeType.Element, "link", null);

            //给元素赋值
            xno.InnerText = "新来的Node";
            addNode.InnerText = "15858585588"; 

            //把节点添加到末尾
            node.AppendChild(xno);
            node.AppendChild(addNode); 


            //在根节点下最后一个子元素末尾添加
            xmlDoc.DocumentElement.AppendChild(node);
            xmlDoc.Save("xmlDoumer.xml"); //保存
        }

 

 同样不厌其烦的看结果:

<?xml version="1.0" encoding="utf-8"?>
<Root>
  <FlyInfo>
    <AirLine>航空</AirLine>
    <Seat>经济舱</Seat>
    <Rating>A</Rating>
    <Gai>可以改</Gai>
    <Tui>可以退</Tui>
    <Qian>可以签</Qian>
  </FlyInfo>
  <FlyInfo>
    <AirLine>北京航空</AirLine>
    <Seat>头等舱</Seat>
    <Rating>AB</Rating>
    <Gai>不改</Gai>
    <Tui>不退</Tui>
    <Qian>不签</Qian>
    <link>新来的Node</link>
    <Phone>15858585588</Phone>
  </FlyInfo>
</Root>

 

 
LINQ to XML
  LINQ to XML是用来操作XDocument类,利用LINQ to XML来对xml进行CRUD 增加(Create)、查询(Retrieve)(重新得到数据)、更新(Update)和删除(Delete)代码简化了许多
在上面你已经看到用linq to xml创建xml文档了。这里为了更明确。用linq to xml来进行完整的增删改查,所以先创建一个xml文档
/// <summary>
        /// linq to xml--创建xml文档
        /// </summary>
        public static void createXML()
        {
            var xDoc = new XDocument(new XElement("Root",
               new XElement("Student",
                   new XAttribute("Id", 1),  //添加属性
                   new XElement("name", "Tom"),
                   new XElement("age", 18)
                   ),
               new XElement("Student",
                   new XAttribute("Id", 2),  //属性
                   new XElement("name", "Jim"),
                   new XElement("age", 20)
                   )
               )
            );
            xDoc.Save(Console.Out); //为了方便,我直接输出到控制台观看结果
            xDoc.Save("Student.xml"); //保存
        }

我们看运行结果。xml文档就顺理成章的生成了

 
 
 
接下来看读取xml文档
/// <summary>
        /// linq to xml--读取xml文档
        /// </summary>
        public static void ReadXML()
        {

            XDocument xDoc = XDocument.Load("Student.xml");  //加载xml文档,(相对路劲)

            /*这里用Descendants(xName)的好处是可以跨层次,跨节点,而不像Element
              比如要找到所有的Student节点。
              Element: xDoc.Element("Root").Elements()  这里必须是从根节点到子节点一级一级找。
              Descendants:xDoc.Descendants("Student")  跨了 根节点 Root 直接在根节点下找
            */
            var E1 = from item in xDoc.Descendants("Student")  //找到所有Student元素
                     select item;

            E1.ToList().ForEach(it => Console.WriteLine(it));

            Console.WriteLine("-----------分割线1-----------");

            //找到学生属性Id=1的学生。显示学生姓名和年龄
            var E2 = from item in xDoc.Descendants("Student")
                     where item.Attribute("Id").Value == "1"
                     select new
                     {
                         name = item.Element("name").Value,
                         age = item.Element("age").Value
                     };
            E2.ToList().ForEach(it => Console.WriteLine(string.Format("姓名:{0},年龄:{1}", it.name, it.age)));

            Console.WriteLine("-----------分割线2-----------");

            //如果学生Id=1有多个,而你只想显示第一个。则用FirstOrDefault(),返回满足条件的第一个元素
            var E3 = (from item in xDoc.Descendants("Student")
                      where item.Attribute("Id").Value == "1"
                      select new
                      {
                          name = item.Element("name").Value,
                          age = item.Element("age").Value
                      }).FirstOrDefault();

            //因为此时返回的结果已不是集合,可直接输出
            Console.WriteLine("姓名为:" + E3.name + "年龄为:" + E3.age);

            Console.WriteLine("-----------分割线3-----------");

            //显示所有学生的姓名和年龄
            var E4 = from item in xDoc.Descendants("Student")
                     select new
                     {
                         name = item.Element("name").Value,
                         age = item.Element("age").Value
                     };
            E4.ToList().ForEach(it => Console.WriteLine(string.Format("姓名:{0},年龄为:{1}", it.name, it.age)));

            //以上仅仅是举了些简单的例子,实际中还得根据需要灵活运用。记住。xml是区分大小写的。所以要注意属性名和元素名的大小写要一致

        }

看结果图:

 

 最后看编辑xml文档
/// <summary>
        ///  linq to xml--编辑xml文档
        /// </summary>
        public static void EditXML()
        {
            XDocument xDoc = XDocument.Load("Student.xml"); //加载xml文档

            //现在你发现学生的姓名和年龄不能满足需求,需要添加一个性别信息 ,那就得添加一个元素<sex>male</sex>
            var E5 = from item in xDoc.Descendants("Student")  //找到所有(元素名为Student)学生节点
                     select item;

            E5.ToList().ForEach(it => it.SetElementValue("sex", "male")); //当然。这是给所有的学生都添加性别为male
            xDoc.Save(Console.Out); //这里为了方便查看修改后的内容。输出到控制台看结果,此时是在内存中。并没有保存到磁盘  下同

            Console.WriteLine("-----------分割线1-----------");

            //这是把第一个学生为mail 第二个学生为female
            foreach (var item in E5)
            {
                if (item.Attribute("Id").Value.Equals("1"))
                    item.SetElementValue("sex", "male");
                else
                    item.SetElementValue("sex", "female");
            }
            xDoc.Save(Console.Out);

            Console.WriteLine("-----------分割线2-----------");

            //知道添加元素了。现在你添加一个学生也是如此简单,班级来了个插班生。需添加第三个学生,多加一张课桌
            var E6 = from item in xDoc.Descendants("Root")  //找到根节点(班级)下所有的(学生)的元素
                     select item;

            //先拼好要添加的元素
            var content = new XElement("Student",
                              new XAttribute("Id", "3"),
                              new XElement("name", "Jack"),
                              new XElement("age", "22"),
                              new XElement("sex", "gay")
                            );

            E6.ToList().ForEach(it => it.LastNode.AddAfterSelf(content)); //在最后一个(学生)节点后添加新(座位)元素
            xDoc.Save(Console.Out);

            Console.WriteLine("-----------分割线3-----------");

            //当发现学生Id=1的学生姓名不能是英文。需修改成中文
            var E7 = from item in xDoc.Descendants("Student")
                     where item.Attribute("Id").Value.Equals("1")
                     select item;

            //找到name元素,修改value。同理。修改age一样。。扩展:SetAttributeValue(name,value)添加属性
            E7.ToList().ForEach(it => it.Element("name").SetValue("汤姆"));
            xDoc.Save(Console.Out);
            Console.WriteLine("-----------分割线4-----------");

            //当汤姆退学。需删除元素(课桌)
            var E18 = from item in xDoc.Descendants("Student")
                      where item.Element("name").Value.Equals("汤姆")  //找到汤姆这个学生
                      select item;

            //删除满足条件的节点(同学),最后只剩下id=2和id=3的节点(学生) 以下三句效果相同
            E18.Remove();
            //E8.ToList().ForEach(it => it.Element("name").Parent.Remove());  
            //E8.ToList().ForEach(it => it.Element("age").Parent.Remove());
            xDoc.Save(Console.Out);

            //记住:1、一个xml文档(XDocument)只能有一个跟节点(Root),文档中不能有相同的元素名(XElement),如果你添加相同的XElement。会覆盖之前的value

            //xDoc.Save("Student.xml"); //保存修改后内容:这里是把xml加载到内存。在内存中修改后再保存到磁盘的。这里保存名不一定是 Student.xml 名字可以随便取。跟之前的Student.xml已经没有任何关系。
        }

 

继续看图:因为图片过大,所以图片是分开截图。

 
 
 
XML序列化(Serialize)与反序列化(Deserialize)
 
 
我这里分别对实体类(非集合类我这样称呼),和集合类进行序列化和反序列化
 
先提供一个泛型XMLHelper
 
 /// <summary>
    /// 序列化与反序列化帮助类--XMLHelper
    /// </summary>
    public class XmlHelper
    {
        /// <summary>
        /// serializer
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">要序列化的实例</param>
        public static void serializeToXml<T>(T obj)
        {
            XmlSerializer serialize = new XmlSerializer(typeof(T));
            using (XmlTextWriter xtw = new XmlTextWriter("Info.xml", Encoding.Default))
                serialize.Serialize(xtw, obj);
        }
        /// <summary>
        /// Deserialize
        /// </summary>
        /// <typeparam name="T">泛型-反序列化后的类型</typeparam>
        /// <param name="data">反序列化的xml文档</param>
        /// <returns></returns>
        public static T DeserializerXml<T>(string data)
        {
            XmlSerializer Deserializer = new XmlSerializer(typeof(T));
            using (XmlTextReader xtr = new XmlTextReader(data))
                return (T)Deserializer.Deserialize(xtr);
        }
    }

 

编写测试类 即我所说的实体类和集合类

 
 /// <summary>
    /// 实体类序列化
    /// </summary>
    [Serializable]
    [XmlRoot("Root")] //这表明序列化xml文档时。自定义节点名
    public class Person
    {
        //[XmlIgnore] //此字段不序列化
        public string name { get; set; }
        public int age { get; set; }
    }
    /// <summary>
    /// 集合类序列化
    /// </summary>
    public class Persons
    {
        public List<Person> data { get; set; }
    }

Main函数测试

 

static void Main(string[] args)
        {
            Person ps = new Person() { name = "李四", age = 20 };

            #region 实体类的序列化和反序列化
            XmlHelper.serializeToXml(ps);
            Person p = XmlHelper.DeserializerXml<Person>("Info.xml");
            Console.WriteLine("实体类反序列化结果:");
            Console.WriteLine("姓名:" + p.name + "年龄:" + p.age);
            #endregion
            Console.WriteLine("---------分割线-------");
            #region 集合类的序列化和反序列化
            Persons pos = new Persons() { data = new List<Person> { ps } };
            //pos.data = new List<Person>() { ps };
            XmlHelper.serializeToXml(pos);
            Persons po = XmlHelper.DeserializerXml<Persons>("Info.xml");
            Console.WriteLine("集合类反序列化结果:");
            po.data.ForEach(item => Console.WriteLine("姓名:" + item.name + "年龄:" + item.age));
            #endregion
        }

 

最后序列化生成Info.xml文档,这里是实体类的序列化的xml,可以看到我自定义的根节点名字 Root

  <?xml version="1.0" encoding="gb2312" ?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <name>李四</name> <age>20</age> </Root>

 

反序列化数据:

 

 

 最后附上完整代码
linq to xml 的完整代码 :
View Code
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Xml.Linq;
  6 
  7 namespace Linq_to_XML
  8 {
  9     class Program
 10     {
 11         static void Main(string[] args)
 12         {
 13             //createXML();
 14             //ReadXML();
 15             EditXML();
 16         }
 17 
 18         /// <summary>
 19         /// linq to xml--创建xml文档
 20         /// </summary>
 21         public static void createXML()
 22         {
 23             var xDoc = new XDocument(new XElement("Root",
 24                new XElement("Student",
 25                    new XAttribute("Id", 1),  //添加属性
 26                    new XElement("name", "Tom"),
 27                    new XElement("age", 18)
 28                    ),
 29                new XElement("Student",
 30                    new XAttribute("Id", 2),  //属性
 31                    new XElement("name", "Jim"),
 32                    new XElement("age", 20)
 33                    )
 34                )
 35             );
 36             xDoc.Save(Console.Out); //为了方便,我直接输出到控制台观看结果
 37             xDoc.Save("Student.xml"); //保存
 38         }
 39 
 40         /// <summary>
 41         /// linq to xml--读取xml文档
 42         /// </summary>
 43         public static void ReadXML()
 44         {
 45 
 46             XDocument xDoc = XDocument.Load("Student.xml");  //加载xml文档,(相对路劲)
 47 
 48             /*这里用Descendants(xName)的好处是可以跨层次,跨节点,而不像Element
 49               比如要找到所有的Student节点。
 50               Element: xDoc.Element("Root").Elements()  这里必须是从根节点到子节点一级一级找。
 51               Descendants:xDoc.Descendants("Student")  跨了 根节点 Root 直接在根节点下找
 52             */
 53             var E1 = from item in xDoc.Descendants("Student")  //找到所有Student元素
 54                      select item;
 55 
 56             E1.ToList().ForEach(it => Console.WriteLine(it));
 57 
 58             Console.WriteLine("");
 59             Console.WriteLine("-----------分割线1-----------");
 60 
 61             //找到学生属性Id=1的学生。显示学生姓名和年龄
 62             var E2 = from item in xDoc.Descendants("Student")
 63                      where item.Attribute("Id").Value == "1"
 64                      select new
 65                      {
 66                          name = item.Element("name").Value,
 67                          age = item.Element("age").Value
 68                      };
 69             E2.ToList().ForEach(it => Console.WriteLine(string.Format("姓名:{0},年龄:{1}", it.name, it.age)));
 70 
 71             Console.WriteLine("");
 72             Console.WriteLine("-----------分割线2-----------");
 73 
 74             //如果学生Id=1有多个,而你只想显示第一个。则用FirstOrDefault(),返回满足条件的第一个元素
 75             var E3 = (from item in xDoc.Descendants("Student")
 76                       where item.Attribute("Id").Value == "1"
 77                       select new
 78                       {
 79                           name = item.Element("name").Value,
 80                           age = item.Element("age").Value
 81                       }).FirstOrDefault();
 82 
 83             //因为此时返回的结果已不是集合,可直接输出
 84             Console.WriteLine("姓名为:" + E3.name + "年龄为:" + E3.age);
 85 
 86             Console.WriteLine("");
 87             Console.WriteLine("-----------分割线3-----------");
 88 
 89             //显示所有学生的姓名和年龄
 90             var E4 = from item in xDoc.Descendants("Student")
 91                      select new
 92                      {
 93                          name = item.Element("name").Value,
 94                          age = item.Element("age").Value
 95                      };
 96             E4.ToList().ForEach(it => Console.WriteLine(string.Format("姓名:{0},年龄为:{1}", it.name, it.age)));
 97 
 98             //以上仅仅是举了些简单的例子,实际中还得根据需要灵活运用。记住。xml是区分大小写的。所以要注意属性名和元素名的大小写要一致
 99 
100         }
101 
102         /// <summary>
103         ///  linq to xml--编辑xml文档
104         /// </summary>
105         public static void EditXML()
106         {
107             XDocument xDoc = XDocument.Load("Student.xml"); //加载xml文档
108 
109             //现在你发现学生的姓名和年龄不能满足需求,需要添加一个性别信息 ,那就得添加一个元素<sex>male</sex>
110             var E5 = from item in xDoc.Descendants("Student")  //找到所有(元素名为Student)学生节点
111                      select item;
112 
113             E5.ToList().ForEach(it => it.SetElementValue("sex", "male")); //当然。这是给所有的学生都添加性别为male
114             xDoc.Save(Console.Out); //这里为了方便查看修改后的内容。输出到控制台看结果,此时是在内存中。并没有保存到磁盘  下同
115 
116             Console.WriteLine("-----------分割线1-----------");
117 
118             //这是把第一个学生为mail 第二个学生为female
119             foreach (var item in E5)
120             {
121                 if (item.Attribute("Id").Value.Equals("1"))
122                     item.SetElementValue("sex", "male");
123                 else
124                     item.SetElementValue("sex", "female");
125             }
126             xDoc.Save(Console.Out);
127 
128             Console.WriteLine("-----------分割线2-----------");
129 
130             //知道添加元素了。现在你添加一个学生也是如此简单,班级来了个插班生。需添加第三个学生,多加一张课桌
131             var E6 = from item in xDoc.Descendants("Root")  //找到根节点(班级)下所有的(学生)的元素
132                      select item;
133 
134             //先拼好要添加的元素
135             var content = new XElement("Student",
136                               new XAttribute("Id", "3"),
137                               new XElement("name", "Jack"),
138                               new XElement("age", "22"),
139                               new XElement("sex", "gay")
140                             );
141 
142             E6.ToList().ForEach(it => it.LastNode.AddAfterSelf(content)); //在最后一个(学生)节点后添加新(座位)元素
143             xDoc.Save(Console.Out);
144 
145             Console.WriteLine("-----------分割线3-----------");
146 
147             //当发现学生Id=1的学生姓名不能是英文。需修改成中文
148             var E7 = from item in xDoc.Descendants("Student")
149                      where item.Attribute("Id").Value.Equals("1")
150                      select item;
151 
152             //找到name元素,修改value。同理。修改age一样。。扩展:SetAttributeValue(name,value)添加属性
153             E7.ToList().ForEach(it => it.Element("name").SetValue("汤姆"));
154             xDoc.Save(Console.Out);
155             Console.WriteLine("-----------分割线4-----------");
156 
157             //当汤姆退学。需删除元素(课桌)
158             var E18 = from item in xDoc.Descendants("Student")
159                       where item.Element("name").Value.Equals("汤姆")  //找到汤姆这个学生
160                       select item;
161 
162             //删除满足条件的节点(同学),最后只剩下id=2和id=3的节点(学生) 以下三句效果相同
163             E18.Remove();
164             //E8.ToList().ForEach(it => it.Element("name").Parent.Remove());  
165             //E8.ToList().ForEach(it => it.Element("age").Parent.Remove());
166             xDoc.Save(Console.Out);
167 
168             //记住:1、一个xml文档(XDocument)只能有一个跟节点(Root),文档中不能有相同的元素名(XElement),如果你添加相同的XElement。会覆盖之前的value
169 
170             //xDoc.Save("Student.xml"); //保存修改后内容:这里是把xml加载到内存。在内存中修改后再保存到磁盘的。这里保存名不一定是 Student.xml 名字可以随便取。跟之前的Student.xml已经没有任何关系。
171         }
172     }
173 }

序列化和反序列化完整代码:

View Code
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Xml.Serialization;
 6 using System.IO;
 7 using System.Xml;
 8 
 9 namespace XMLSerialize
10 {
11     class Program
12     {
13         static void Main(string[] args)
14         {
15             Person ps = new Person() { name = "李四", age = 20 };
16 
17             #region 实体类的序列化和反序列化
18             XmlHelper.serializeToXml(ps);
19             Person p = XmlHelper.DeserializerXml<Person>("Info.xml");
20             Console.WriteLine("实体类反序列化结果:");
21             Console.WriteLine("姓名:" + p.name + "年龄:" + p.age);
22             #endregion
23             Console.WriteLine("---------分割线-------");
24             #region 集合类的序列化和反序列化
25             Persons pos = new Persons() { data = new List<Person> { ps } };
26             //pos.data = new List<Person>() { ps };
27             XmlHelper.serializeToXml(pos);
28             Persons po = XmlHelper.DeserializerXml<Persons>("Info.xml");
29             Console.WriteLine("集合类反序列化结果:");
30             po.data.ForEach(item => Console.WriteLine("姓名:" + item.name + "年龄:" + item.age));
31             #endregion
32         }
33     }
34     /// <summary>
35     /// 实体类序列化
36     /// </summary>
37     [Serializable]
38     [XmlRoot("Root")] //这表明序列化xml文档时。自定义节点名
39     public class Person
40     {
41         //[XmlIgnore] //此字段不序列化
42         public string name { get; set; }
43         public int age { get; set; }
44     }
45     /// <summary>
46     /// 集合类序列化
47     /// </summary>
48     public class Persons
49     {
50         public List<Person> data { get; set; }
51     }
52     /// <summary>
53     /// 序列化与反序列化帮助类--XMLHelper
54     /// </summary>
55     public class XmlHelper
56     {
57         /// <summary>
58         /// serializer
59         /// </summary>
60         /// <typeparam name="T"></typeparam>
61         /// <param name="obj">要序列化的实例</param>
62         public static void serializeToXml<T>(T obj)
63         {
64             XmlSerializer serialize = new XmlSerializer(typeof(T));
65             using (XmlTextWriter xtw = new XmlTextWriter("Info.xml", Encoding.Default))
66                 serialize.Serialize(xtw, obj);
67         }
68         /// <summary>
69         /// Deserialize
70         /// </summary>
71         /// <typeparam name="T">泛型-反序列化后的类型</typeparam>
72         /// <param name="data">反序列化的xml文档</param>
73         /// <returns></returns>
74         public static T DeserializerXml<T>(string data)
75         {
76             XmlSerializer Deserializer = new XmlSerializer(typeof(T));
77             using (XmlTextReader xtr = new XmlTextReader(data))
78                 return (T)Deserializer.Deserialize(xtr);
79         }
80     }
81 }

到这里就结束了。初出茅庐。还请多多指教,谢谢!!