目录
概念
基础知识
总结
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
在学习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的区别:
特性 |
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] 属性的成员,即使成员标记为私有。
[DataMember]
的内容才会被序列化[DataMember]
的内容都将被序列化——不管是public
还是private
通常,在XML序列化的过程中,有很多东西是自动生成的,例如XML命名空间,编码等等。
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", ""); //第一个参数是前缀,第二个参数是命名空间
//然后在序列化的时候,指定自定义命名空间
xml.Serialize(ms, p, ns);
输出对比:
当然,这个方法也可以用于生成你想要的自定义命名空间。
顶部的
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());
}
输出:
settings.Indent = true;
当XmlWriterSettings如此设置后,输出的XML为:
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的结构,这时候就要用到我们的属性类了。属性类提供了很多特性供我们使用,以完成自定义序列化功能。
特性 | 适用对象 | 指定 |
---|---|---|
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
将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
三年
*/
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);
}
}
数组名称 [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
其他代码
*/
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;
}
/*输出
语文老师
//注意
数学老师
数学
*/
Dictionary
不支持序列化 ,只能自己写
参考:C# Xml序列化Dictionary - CashQian Blog
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;
}
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 ; }
}