【C# XML 序列化】开篇
概念
XML 序列化中的中心类是 XmlSerializer 类,此类中最重要的方法是 Serialize 和 Deserialize 方法 。 XmlSerializer 创建 C# 文件并将其编译为 .dll 文件,以执行此序列化。 XML 序列化程序生成器工具 (Sgen.exe) 旨在预先生成要与应用程序一起部署的这些序列化程序集,并改进启动性能。 XmlSerializer 生成的 XML 流符合万维网联合会 (W3C) XML 架构定义语言 (XSD) 1.0 建议。 而且,生成的数据类型符合文档“XML 架构第 2 部分:数据类型”。
官方xml序列化教程:https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/the-xml-schema-definition-tool-and-xml-serialization
基础知识
在学习xml序列化时候,你必须学习过,xml命名空间、xml Schemas(架构) 、xml类型定义、xml 良好的文档格式 、DTD(文档类型定义)、xpath
1、将一个类序列化到xml文件中
总结
XML序列化一些注意事项
(1)要序列化的类必须有默认的构造的构造函数,才能使用XmlSerializer序列化,需要序列化的类都必须有一个无参的构造函数(通过对基础中类和类的实例学习,我们必须知道类不定义构造函数的情况下,会默认生成一个无参数的构造函数);
补充:如果变量只声明,没有赋值,序列化后是没有对应的节点和属性值。
(2)索引器、私有字段或只读属性(只读集合属性除外)不能被序列化;
(3)不想序列化时:当不想序列化一个属性时,使用[System.Xml.Serialization.XmlIgnore]标记,能用于属性;[NonSerializable]应用于属性无效,能用于类,结构体等;
(4)方法不能被序列化(虽然是废话,但是还是列举出来);
(5)枚举变量可序列化为字符串,无需用[XmlInclude]
(6)导出非基本类型对象,都必须用[XmlInclude]事先声明。该规则递归作用到子元素 。可以参考
(7)Attribute中的IsNullable参数若等于false,表示若元素为null则不显示该元素。(针对值类型有效)
(8)某些类就是无法XML序列化的(即使使用了[XmlInclude])
比如:
IDictionary(如HashTable);
父类对象赋予子类对象值的情况;
对象间循环引用;
(9)对于无法XML序列化的对象,可考虑:
使用自定义xml序列化(实现IXmlSerializable接口);
实现IDictionary的类,可考虑(1)用其它集合类替代;(2)用类封装之,并提供Add和this函数;
某些类型需要先经过转换,然后才能序列化为 XML。如XML序列化System.Drawing.Color,可先用ToArgb()将其转换为整数;
过于复杂的对象用xml序列化不便的话,可考虑用二进制序列化;
(10)默认构造函数是必须的,因为反序列化本质上使用的是反射,需要默认构造函数来实例化类,如果去掉其中的默认构造函数,则编译没有问题,但运行就会报错。
尽量不要将比较大的属性放在默认构造函数初始化,那会导致在反序列化时对列表初始化两次:默认构造函数中执行一次,反序列化时从XML文档读取再执行一次。
以上十点注意,其中前三点,也就是加黑的这几点,是重点要知道的。
XML序列化的xml文档配置
通常,在XML序列化的过程中,有很多东西是自动生成的,例如XML命名空间,编码等等。
1、去除默认的命名空间与前缀
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", ""); //第一个参数是前缀,第二个参数是命名空间
//然后在序列化的时候,指定自定义命名空间
xml.Serialize(ms, p, ns);
输出对比:
当然,这个方法也可以用于生成你想要的自定义命名空间。
2、去除XML声明
顶部的 <?xml version="1.0" encoding="utf-8"?>
public static string ObjectToXmlSerializer(Object Obj)
{
XmlWriterSettings settings = new XmlWriterSettings();
//去除xml声明
settings.OmitXmlDeclaration = true;
settings.Encoding = Encoding.Default;
System.IO.MemoryStream mem = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(mem, settings))
{
//去除默认命名空间xmlns:xsd和xmlns:xsi
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer formatter = new XmlSerializer(Obj.GetType());
formatter.Serialize(writer, Obj, ns);
}
return Encoding.Default.GetString(mem.ToArray());
}
输出:
3、换行缩进
settings.Indent = true;
当XmlWriterSettings如此设置后,输出的XML为:
4、指定缩进字符
settings.IndentChars = "--";
输出如下:
XmlWriterSettings更多设置属性如下:
| 成员 | 说明 |
| CloseOutput | 获取或设置一个值,该值指示在调用 Close 方法时,XmlWriter 是否还应该关闭基础流或 TextWriter。 |
| Encoding | 获取或设置要使用的文本编码的类型。 |
| Indent | 获取或设置一个值,该值指示是否缩进元素。 |
| IndentChars | 获取或设置缩进时要使用的字符串。 |
| NamespaceHandling | 获取或设置一个值,该值指示在编写 XML 内容时,XmlWriter 是否应移除重复的命名空间声明。 的默认是输出程序中出现的所有命名空间声明。 |
| NewLineChars | 获取或设置要用于分行符的字符串 |
| NewLineHandling | 获取或设置一个值,该值指示是否将输出中的分行符正常化。 |
| NewLineOnAttributes | 获取或设置一个值,该值指示是否将属性写入新行。 |
| OmitXmlDeclaration | 获取或设置一个值指示省略 XML 声明。 |
| Reset方法 | 重置以上属性 |
http://msdn.microsoft.com/zh-cn/library/system.xml.xmlwritersettings(v=vs.110).aspx
XML 序列化
XML特性
有时,我们在序列化时想要自定义XML的结构,这时候就要用到我们的属性类了。属性类提供了很多特性供我们使用,以完成自定义序列化功能。
| 名称 | 描述 |
| XmlAttribute | 表示一个特性对象的集合,这些对象控制XmlSerializer如何序列化和反序列化对象 |
| XmlArrayAttribute | 指定XmlSerializer应将特定的类成员序列化为XML元素数组 |
| XmlArrayItemAttribute | 指定XmlSerializer可以放置在序列化数组中的派生类型 |
| XmlArrayItemAttributes | 表示这个是集合类,是XmlArrayItemAttribute对象的集合类 |
| XmlAttributeAttribute | 指定XmlSerializer应将类成员作为XML特性序列化 |
| XmlChoiceIdentifierAttribute | 指定可以通过使用枚举来进一步消除成员的歧义 |
| XmlElementAttribute | 在XmlSerializer序列化或反序列化包含对象时,指示公共字段或属性表示XML元素 |
| XmlElementAttributes | 表示这个是集合类,表示XmlElementAttribute的集合,XmlSerializer将其用于它重写序列化类的默认方式 |
| XmlEnumAttribute | 控制XmlSerializer如何序列化枚举成员 |
| XmlIgnoreAttribute | 指示XmlSerializer方法不序列化公共字段或公共读/写属性值 |
| XmlIncludeAttribute | 允许XmlSerializer在它序列化或反序列化对象时识别类型 |
| XmlRootAttribute | 控制视为XML根元素的属性目标的XML序列化 |
| XmlTextAttribute | 当序列化或反序列化时,想XmlSerializer指示应将此成员作为XML文本处理 |
| XmlTypeAttribute | 控制当属性目标由XmlSerializer序列化时生成的XML结构 |
| XmlAnyAttributeAttribute | 指定成员(返回XmlAttribute对象的数组的字段)可以包含XML属性 |
| XmlAnyElementAttribute | 指定成员可以包含对象,该对象表示在序列化或反序列化的对象中没有相应成员的所有XML元素 |
| XmlAnyElementAttributes | 表示XmlAnyElementAttribute对象的集合 |
| XmlAttributeEventArgs | 为UnKnowAttribute提供数据 |
| XmlAttributeOverrides | 允许你在使用XmlSerializer序列化或反序列化时重写属性、字段和类特性 |
| XmlElementEventArgs | 为UnknownElement事件提供数据 |
| XmlNamespaceDeclarationsAttribute | 指定目标属性、参数、返回值或类成员包含与XML文档中所用命名空间关联的前缀 |
| XmlNodeEventArgs | 为UnknownNode时间提供数据 |
| XmlSerializer | 将对象序列化到XML文档中和从XML文档中反序列化对象,XmlSerializer使你得以控制如何将对象编码到XML中 |
| XmlSerializerNamespaces | 包含XmlSerializer用于在XML文档实例中生成限定名的XML命名空间和前缀 |
| XmlTypeMapping | 包含从一种类型到另一种类型的映射 |
更多更详细的说明,可以在这里看到:http://msdn.microsoft.com/zh-cn/library/System.Xml.Serialization(v=vs.110).aspx
1、字段/属性序列化
将C#Public字段/属性转化成 xml属性。在C#类字段/属性前添加[XmlAttribute]
将C#Public字段/属性转化成 xml元素。在C#类字段/属性前添加[XmlElementAttribute]
不想将C#类的Public字段/属性序列化,则应该在C#类的字段或属性前添加【XmlIgnoreAttribute】
给要作为根目录的 C#类前添加【XmlRootAttribute】,注意改选项在序列化类中 自定义类型字段时候会引发 System.InvalidOperationException:“There was an error generating the XML document.”
using System.Xml; using System.Xml.Serialization; XmlSerializer xmlSerializer = new XmlSerializer(typeof(XClass)); XmlWriterSettings setting=new XmlWriterSettings(); setting.Indent = true; setting.OmitXmlDeclaration = true; XClass xClass = new() { Count=5, Grade="2" , Name= "三年", Description="先进班级" }; xClass.students[0] = new Student() { Name = "小李", Age = 15, Id = 1 } ; xClass.students[1] = new Student() { Name = "丽红", Age = 16, Id = 2 }; xClass.students[2] = new Student() { Name = "进李", Age = 17, Id = 3 }; xClass.students[3] = new Student() { Name = "嗨李", Age = 14, Id = 4 }; xClass.students[4] = new Student() { Name = "所有", Age = 15, Id = 5 }; using XmlWriter xmlWriter =XmlWriter.Create("atients.xml", setting); xmlSerializer.Serialize(xmlWriter, xClass); [XmlRoot("Class",Namespace ="http://studeng.com")] public class XClass { public string? Grade { get; set; } public string? Name { get; set; } public string? Description; public int Count; public Student[] students=new Student[5]; } public class Student { [XmlAttribute] public int Id { get; set; } [XmlElement] public string? Name { get; set; } [XmlAttribute] public int Age; } /* 输出http://www.w3.org/2001/XMLSchema-instance " xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://studeng.com">先进班级 5 小李 丽红 进李 嗨李 所有 2 三年 */
2、数组序/list列化
数组名称 [XmlArray("Items")]、数组项序列化 [XmlArrayItem("Item")]。
数组:
ElementName:数组项目名称
Form:默认设置 XmlSchemaForm.None,XmlSchemaForm.Qualified 元素名称遵循命名空间完全限制
IsNullable:是否给数组null项,序列化为xsi:nil="true"。IsNullable = false 表示不序列化null项
Namespace:数组的命名空间
Order:数组排序,当类中数组排序时候,所有的数组都要参与排序,否则会出错。
数组项:
ElementName:数组项目名称
Form:默认设置 XmlSchemaForm.None,XmlSchemaForm.Qualified 元素名称遵循命名空间完全限制
IsNullable:是否给数组null项,序列化为xsi:nil="true"。IsNullable = false 表示不序列化null项
Namespace:数组的命名空间
DataType:元素类型
Type:数组中允许的 Type。 案例三
using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; XmlSerializer xmlSerializer = new XmlSerializer(typeof(XClass)); XmlWriterSettings setting = new XmlWriterSettings(); setting.Indent = true; setting.OmitXmlDeclaration = true; XClass xClass = new(); //案例一 xClass.XTeacher[1] = new Teacher() { Name = "语文老师" }; xClass.XTeacher[0] = new SubjectTeacher { Name = "数学老师", Subject = "数学" }; //案例二 xClass.XStudent[1] = new Teacher() { Name = "语文课代表" }; //案例三 type xClass.PrimitiveTypes[0]= new Teacher() { Name = "语文老师" }; xClass.PrimitiveTypes[1]= new SubjectTeacher() { Name = "体育老师" }; using XmlWriter xmlWriter = XmlWriter.Create("atients.xml", setting); xmlSerializer.Serialize(xmlWriter, xClass); public class XClass { //案例一 [XmlArray("Items", Order = 2)] [XmlArrayItem(IsNullable = false, ElementName = "Item")]//如果不添加IsNullable = false或者=true,数组的null的项,将生成- , public Teacher[] XTeacher = new Teacher[5]; //案例二 [XmlArray(Form = XmlSchemaForm.Qualified, ElementName = "Student", Order =1, Namespace = "http://www.cohowinery.com")] public Teacher[] XStudent = new Teacher[2]; //案例三 type限制数组的项 [XmlArrayItem(typeof(SubjectTeacher))] [XmlArrayItem(typeof(Teacher))] [XmlArray("Items", Order = 0)] public object[] PrimitiveTypes =new object[5]; } [XmlInclude(typeof(SubjectTeacher))] public class Teacher { public string? Name; } public class SubjectTeacher : Teacher { public string? Subject; } public class TiyueTeacher : Teacher { public string? Subject; } /*输出
http://www.w3.org/2001/XMLSchema-instance " xmlns:xsd="http://www.w3.org/2001/XMLSchema">语文老师 体育老师 http://www.cohowinery.com ">语文课代表 */ 数学老师 数学 语文老师
多维数组:
XmlArrayItemAttribute.NestingLevel 属性:案例 多维度数组序列化
using System; using System.Xml; using System.Xml.Serialization; using System.IO; public class Forest { /* Set the NestingLevel for each array. The first attribute (NestingLevel = 0) is optional. */ [XmlArrayItem(ElementName = "tree", NestingLevel = 0)] [XmlArrayItem(ElementName = "branch", NestingLevel = 1)] [XmlArrayItem(ElementName = "leaf", NestingLevel = 2)] public string[][][] TreeArray; } public class Test { public static void Main() { Test t = new Test(); t.SerializeObject("Tree.xml"); } private void SerializeObject(string filename) { XmlSerializer serializer = new XmlSerializer(typeof(Forest)); Forest f = new Forest(); string[][][] myTreeArray = new string[2][][]; string[][] myBranchArray1 = new string[1][]; myBranchArray1[0] = new string[1] { "One" }; myTreeArray[0] = myBranchArray1; string[][] myBranchArray2 = new string[2][]; myBranchArray2[0] = new string[2] { "One", "Two" }; myBranchArray2[1] = new string[3] { "One", "Two", "Three" }; myTreeArray[1] = myBranchArray2; f.TreeArray = myTreeArray; serializer.Serialize(Console.Out, f); } } /*输出 *http://www.w3.org/2001/XMLSchema-instance " xmlns:xsd="http://www.w3.org/2001/XMLSchema">语文老师 体育老师 http://www.cohowinery.com ">语文课代表 */ 数学老师 数学 语文老师
3、枚举序列化
其他代码
public StudentStatus studentStatus= new StudentStatus();
xClass.studentStatus = StudentStatus.One; public enum StudentStatus { [XmlEnum(Name = "Single")] One, [XmlEnum(Name = "Double")] Two, [XmlEnum(Name = "Triple")] Three } /* 输出结果
其他代码Single
其他代码
*/
4、序列化派生类
using System.Xml; using System.Xml.Serialization; XmlSerializer xmlSerializer = new XmlSerializer(typeof(XClass)); XmlWriterSettings setting = new XmlWriterSettings(); setting.Indent = true; setting.OmitXmlDeclaration = true; XClass xClass = new(); Teacher[] teachers = { new Teacher() { Name = "语文老师", }, new SubjectTeacher { Name = "数学老师", Subject = "数学" } }; xClass.XTeacher = teachers; using XmlWriter xmlWriter = XmlWriter.Create("atients.xml", setting); xmlSerializer.Serialize(xmlWriter, xClass); public class XClass { public Teacher[] XTeacher; } [XmlInclude(typeof(SubjectTeacher))] public class Teacher { public string? Name; } public class SubjectTeacher : Teacher { public string? Subject; } /*输出http://www.w3.org/2001/XMLSchema-instance " xmlns:xsd="http://www.w3.org/2001/XMLSchema">*/ 语文老师 //注意 数学老师 数学
XML 反序列化
1、当xml中 的属性,无法在C#类中找到对应的属性时候,可以将xml属性全部转化成XmlAttribute[]数组。如下:
xml文档
<?xml version="1.0" encoding="utf-8"?>"http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" GroupType = 'Technical' GroupNumber = '42' GroupBase = 'Red'> MyGroup
对应的类
public class Group{ public string GroupName; // The UnknownAttributes array will be used to collect all unknown // attributes found when deserializing. [XmlAnyAttribute] public XmlAttribute[] XAttributes; }
1、当xml中 的元素,无法在C#类中找到对应的属性/字段时候,可以将xml属性全部转化成XmlElement[]数组。如下:
xml文档
对应的类
public class XClass { /* Apply the XmlAnyElementAttribute to a field returning an array of XmlElement objects. */ [XmlAnyElement] public XmlElement[] AllElements; }