1、什么是XML?
2、XDocument和XmlDocument的区别?
3、XDocument
4、XmlDocument
5、LINQ to XML
6、XML序列化与反序列化
因为这几天用到了不熟悉的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方式:
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>
//创建一个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>
/// <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文档就顺理成章的生成了
![](https://i-blog.csdnimg.cn/blog_migrate/ac20d88777b5089e1aaaa15efe2434f0.jpeg)
/// <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是区分大小写的。所以要注意属性名和元素名的大小写要一致 }
看结果图:
/// <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已经没有任何关系。 }
继续看图:因为图片过大,所以图片是分开截图。
![](https://i-blog.csdnimg.cn/blog_migrate/2559cbd1f173ba3be105038cb09768d2.jpeg)
/// <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>
反序列化数据:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
序列化和反序列化完整代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
到这里就结束了。初出茅庐。还请多多指教,谢谢!!