【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;
}
C