C# XML序列化

XML 概念

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

官方xml序列化教程
C# XML序列化_第1张图片

基础知识

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

注意事项

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序列化的对象,可考虑:

	1、使用自定义xml序列化(实现IXmlSerializable接口);
	2、实现IDictionary的类,可考虑:
		(1)用其它集合类替代;
		(2)用类封装之,并提供Add和this函数;

某些类型需要先经过转换,然后才能序列化为 XML。如XML序列化System.Drawing.Color,可先用ToArgb()将其转换为整数;

过于复杂的对象用xml序列化不便的话,可考虑用二进制序列化;

(10)默认构造函数是必须的,因为反序列化本质上使用的是反射,需要默认构造函数来实例化类,如果去掉其中的默认构造函数,则编译没有问题,但运行就会报错。

尽量不要将比较大的属性放在默认构造函数初始化,那会导致在反序列化时对列表初始化两次:默认构造函数中执行一次,反序列化时从XML文档读取再执行一次。

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

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张图片C# XML序列化_第3张图片

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更多设置属性如下:

官方文档地址
C# XML序列化_第4张图片

使用属性控制 XML 序列化

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

C# XML序列化_第5张图片
更多更详细的说明,可以在这里看到

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 不支持序列化 ,只能自己写,参考地址

XML 反序列化

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

  
<Group xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
xmlns:xsd="http://www.w3.org/2001/XMLSchema"   
GroupType = 'Technical' GroupNumber = '42' GroupBase = 'Red'>  
  <GroupName>MyGroupGroupName>  
Group>  

对应类

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#,c#,xml,开发语言)