C# XML 序列化【详细教程】

 

目录

概念

基础知识

总结

DataContractSerializer 与 XMLSerializer的区别

 XmlSerializer序列化的xml文档配置

  1、去除默认的命名空间与前缀

  2、去除XML声明

  3、换行缩进

  4、指定缩进字符

使用属性控制 XML 序列化。

1、字段/属性序列化

2、将Public 属性/字段序列化为 节点的文本

3、数组序/list列化

5、序列化派生类 

 6、Xml序列化Dictionary

XML 反序列化

 练习题

 反序列化博客园 Rss


概念

XML 序列化中的中心类是 XmlSerializer 类,此类中最重要的方法是 Serialize 和 Deserialize 方法 。 XmlSerializer 创建 C# 文件并将其编译为 .dll 文件,以执行此序列化。 XML 序列化程序生成器工具 (Sgen.exe) 旨在预先生成要与应用程序一起部署的这些序列化程序集,并改进启动性能。 XmlSerializer 生成的 XML 流符合万维网联合会 (W3C) XML 架构定义语言 (XSD) 1.0 建议。 而且,生成的数据类型符合文档“XML 架构第 2 部分:数据类型”。

官方xml序列化教程:XML 架构定义工具和 XML 序列化 | Microsoft Learn

C# XML 序列化【详细教程】_第1张图片

基础知识

在学习xml序列化时候,你必须学习过,xml命名空间、xml Schemas(架构) 、xml类型定义、xml 良好的文档格式 、DTD(文档类型定义)、xpath

1、将一个类序列化到xml文件中

总结

 XML序列化一些注意事项

(1)要序列化的类必须有默认的构造的构造函数,才能使用XmlSerializer序列化,需要序列化的类都必须有一个无参的构造函数(通过对基础中类和类的实例学习,我们必须知道类不定义构造函数的情况下,会默认生成一个无参数的构造函数);

  补充:如果变量只声明,没有赋值,序列化后是没有对应的节点和属性值。

(2)索引器、私有字段或只读属性(只读集合属性除外)不能被序列化;若要序列化对象的所有公共和私有字段和属性,请使用 DataContractSerializer 而不要使用 XML 序列化。

(3)不想序列化时:当不想序列化一个属性时,使用[System.Xml.Serialization.XmlIgnore]标记,能用于属性;[NonSerializable]应用于属性无效,能用于类,结构体等;

(4)方法不能被序列化(虽然是废话,但是还是列举出来);

(5)枚举变量可序列化为字符串,无需用[XmlInclude]

(6)导出非基本类型对象,都必须用[XmlInclude]事先声明。该规则递归作用到子元素 。可以参考  spacer_robot 

(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文档读取再执行一次。

以上十点注意,其中前三点,也就是加黑的这几点,是重点要知道的。

DataContractSerializer 与 XMLSerializer的区别

DataContractSerializer 与 XMLSerializer的区别:

特性

XMLSerializer

DataContractSerializer

默认Mapping

所有Public Field和可读可写Property

所有DataMember Filed、Property

是否需要Attribute

不需要

DataContract DataMember或者Serializable

成员的默认次序

Type中定义的顺序

字母排序

兼容性

.asmx

Remoting

Deserialzation过程中

调用默认构造函数

不会调用

DataContractSerializer 的性能优于 Xmlserializer。这是因为 DataContratSerializer 显式显示了哪些字段或属性被序列化为 XML。DataContractSerializer 可以序列化实现 Idictionary 的类型,而 XML 序列化器不能。DataContractSerializer 序列化所有标记为 [DataMember] 属性的成员,即使成员标记为私有。

  • 针对速度进行了优化(通常比XmlSerializer快10%左右)
  • "选择加入"——只有你特别标记为[DataMember]的内容才会被序列化
  • 但任何标记为[DataMember]的内容都将被序列化——不管是public还是private
  • 不支持XML属性(出于速度原因)
  • 一个类不同时使用[Serializable]和[DataContract]标签。

 XmlSerializer序列化的xml文档配置

  通常,在XML序列化的过程中,有很多东西是自动生成的,例如XML命名空间,编码等等。

  1、去除默认的命名空间与前缀

XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
  ns.Add("", "");  //第一个参数是前缀,第二个参数是命名空间
  //然后在序列化的时候,指定自定义命名空间
  xml.Serialize(ms, p, ns);

  输出对比:

C# XML 序列化【详细教程】_第2张图片

   

  当然,这个方法也可以用于生成你想要的自定义命名空间。

  2、去除XML声明

顶部的 

 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方法 重置以上属性

      XmlWriterSettings 类 (System.Xml) | Microsoft Learn

使用属性控制 XML 序列化。

  有时,我们在序列化时想要自定义XML的结构,这时候就要用到我们的属性类了。属性类提供了很多特性供我们使用,以完成自定义序列化功能。

特性 适用对象 指定
XmlAnyAttributeAttribute 公共字段、属性、参数或返回 XmlAttribute 对象数组的返回值。 反序列化时,将会使用 XmlAttribute 对象填充数组,而这些对象代表对于架构未知的所有 XML 特性。
XmlAnyElementAttribute 公共字段、属性、参数或返回 XmlElement 对象数组的返回值。 反序列化时,将会使用 XmlElement 对象填充数组,而这些对象代表对于架构未知的所有 XML 元素。
XmlArrayAttribute 公共字段、属性、参数或返回复杂对象的数组的返回值。 数组成员将作为 XML 数组的成员生成。
XmlArrayItemAttribute 公共字段、属性、参数或返回复杂对象的数组的返回值。 可以插入数组的派生类型。 通常与 XmlArrayAttribute 一起应用。
XmlAttributeAttribute 公共字段、属性、参数或返回值。 成员将作为 XML 属性进行序列化。
XmlChoiceIdentifierAttribute 公共字段、属性、参数或返回值。 可以使用枚举进一步消除成员的歧义。
XmlElementAttribute 公共字段、属性、参数或返回值。 字段或属性将作为 XML 元素进行序列化。
XmlEnumAttribute 作为枚举标识符的公共字段。 枚举成员的元素名称。
XmlIgnoreAttribute 公共属性和公共字段。 序列化包含类时,应该忽略属性或字段。
XmlIncludeAttribute 公共派生类声明,以及 Web 服务描述语言 (WSDL) 文档的公共方法的返回值。 生成要在序列化时识别的架构时,应该将该类包括在内。
XmlRootAttribute 公共类声明。 控制视为 XML 根元素的属性目标的 XML 序列化。 使用该属性可进一步指定命名空间和元素名称。
XmlTextAttribute 公共属性和公共字段。 属性或字段应该作为 XML 文本进行序列化。
XmlTypeAttribute 公共类声明。 XML 类型的名称和命名空间。

  更多更详细的说明,可以在这里看到:System.Xml.Serialization 命名空间 | Microsoft Learn

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;

}
/* 输出

  先进班级
  5
  
    
      小李
    
    
      丽红
    
    
      进李
    
    
      嗨李
    
    
      所有
    
  
  2
  三年

*/

2、将Public 属性/字段序列化为 节点的文本

XmlText:属性做节点的文本。节点文本

using System;
using System.Xml.Serialization;
using System.IO;
using System.Xml;

public class Group1
{
    // The XmlTextAttribute with type set to string informs the
    // XmlSerializer that strings should be serialized as XML text.
    [XmlText(typeof(string))]
    [XmlElement(typeof(int))]
    [XmlElement(typeof(double))]
    public object[] All = new object[] { 321, "One", 2, 3.0, "Two" };
}

public class Group2
{
    [XmlText(Type = typeof(GroupType))]
    public GroupType Type;
}
public enum GroupType
{
    Small,
    Medium,
    Large
}

public class Group3
{
    [XmlText(Type = typeof(DateTime))]
    public DateTime CreationTime = DateTime.Now;
}

public class Test
{
    static void Main()
    {
        Test t = new Test();
        t.SerializeArray("XmlText1.xml");
        t.SerializeEnum("XmlText2.xml");
        t.SerializeDateTime("XmlText3.xml");
    }

    private void SerializeArray(string filename)
    {
        XmlSerializer ser = new XmlSerializer(typeof(Group1));
        Group1 myGroup1 = new Group1();
        XmlWriterSettings  xmlWriterSettings = new XmlWriterSettings();
        xmlWriterSettings.Indent = true;        

   
        using FileStream stream =File.Open(filename,FileMode.OpenOrCreate);
        XmlWriter xmlWriter =   XmlWriter.Create(stream, xmlWriterSettings);    

        ser.Serialize(xmlWriter, myGroup1);
       
    }

    private void SerializeEnum(string filename)
    {
        XmlSerializer ser = new XmlSerializer(typeof(Group2));
        Group2 myGroup = new Group2();
        myGroup.Type = GroupType.Medium;
        XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
        xmlWriterSettings.Indent = true;


        using FileStream stream = File.Open(filename, FileMode.OpenOrCreate);
        XmlWriter xmlWriter = XmlWriter.Create(stream, xmlWriterSettings);

        ser.Serialize(xmlWriter, myGroup);
     
    }

    private void SerializeDateTime(string filename)
    {
        XmlSerializer ser = new XmlSerializer(typeof(Group3));
        Group3 myGroup = new Group3();
        XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
        xmlWriterSettings.Indent = true;


        using FileStream stream = File.Open(filename, FileMode.OpenOrCreate);
        XmlWriter xmlWriter = XmlWriter.Create(stream, xmlWriterSettings);

        ser.Serialize(xmlWriter, myGroup);
       
    }
}

3、数组序/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;
}
/*输出

  
    
      语文老师
    
    
      体育老师
    
  
  
    
    
      语文课代表
    
  
  
    
      数学老师
      数学
    
    
      语文老师
    
  

 */

多维数组:

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);
    }
}
/*输出 
 * 
  
    
      语文老师
    
    
      体育老师
    
  
  
    
    
      语文课代表
    
  
  
    
      数学老师
      数学
    
    
      语文老师
    
  

 */

4、枚举序列化
其他代码
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

其他代码

*/

 

5、序列化派生类 

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;
}
/*输出
    
  
    
      语文老师
    
      //注意
      数学老师
      数学
    
  
*/

 6、Xml序列化Dictionary

Dictionary 不支持序列化 ,只能自己写

参考:C# Xml序列化Dictionary - CashQian Blog

XML 反序列化

 1、当xml中 的属性,无法在C#类中找到对应的属性时候,可以将xml属性全部转化成XmlAttribute[]数组。如下:

xml文档

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

 练习题

 反序列化博客园 Rss

1、第一步用 复制博客园rss源代码,然后再vs2022 编辑>选择性粘贴>粘贴为xml类

2、修改xml类型

using System.Xml.Serialization;

Stream stream = File.OpenRead(@"D:\程序开发\博客园备份\CNBlogs_BlogBackup_131_202108_202203(2).xml");
XmlSerializer xmlSerializer = new  (typeof(BoKeYuanRss));
BoKeYuanRss boke = (BoKeYuanRss)xmlSerializer.Deserialize(stream);
Console.WriteLine(boke.Version);
Console.WriteLine(boke.Channel.Title);
Console.Read();


// 注意: 生成的代码可能至少需要 .NET Framework 4.5 或 .NET Core/Standard 2.0。
/// 
[Serializable()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[XmlTypeAttribute(AnonymousType = true)]
[XmlRootAttribute(ElementName = "rss", Namespace = "", IsNullable = false)]
public partial class BoKeYuanRss
{
    [XmlAttributeAttribute("version")]
   
    public decimal Version { get; set ; }

    [XmlElement("channel")]
    public RssChannel Channel { get ; set; }
}

/// 

public partial class RssChannel
{
    [XmlElement("title")]
    public string Title { get; set; }
    [XmlElement("link")]
    public string Link { get; set; }
    [XmlElement("description")]
    public string Description { get; set ; }
    [XmlElement("language")]
    public string Language { get ; set; }
    [XmlElement("lastBuildDate")]
    public string LastBuildDate { get; set ; }
    [XmlElement("pubDate")]
    public string PubDate { get; set; }
   
    [XmlArrayItem("item")]
    public RssChannelItem[] Item { get; set; }
    [XmlElement("ttl ")]
    public byte Ttl { get; set; }
}


public partial class RssChannelItem
{
    [XmlElement("title")]
    public string Title { get; set ; }
    [XmlElement("link")]
    public string Link { get; set; }
 
    [XmlElement("creator", Namespace = "http://purl.org/dc/elements/1.1/")]
    public string Creator { get; set; }
    [XmlElement("author")]
    public string Author { get; set; }
    [XmlElement("pubDate")]
    public string PubDate { get; set ; }
    [XmlElement("guid")]
    public string Guid { get; set ; }
    [XmlElement("description")]
    public string Description { get; set ; }
}

你可能感兴趣的:(#,C#语言,【知识梳理篇】,microsoft,c#,开发语言)