C# XML序列化

XML 概念

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

官方xml序列化教程
XML序列化

基础知识

在学习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的区别

特性XMLSerializerDataContractSerializer
默认Mapping所有Public Field和可读可写Property所有DataMember Filed、Property
是否需要Attribute不需要DataContract DataMember或者Serializable
成员的默认次序Type中定义的顺序字母排序
兼容性.asmxRemoting
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);

输出对比
在这里插入图片描述在这里插入图片描述

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

官方文档地址
在这里插入图片描述

使用属性控制 XML 序列化

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

在这里插入图片描述
更多更详细的说明,可以在这里看到

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;

}
/* 输出
<Class xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://studeng.com">
  <Description>先进班级</Description>
  <Count>5</Count>
  <students>
    <Student Age="15" Id="1">
      <Name>小李</Name>
    </Student>
    <Student Age="16" Id="2">
      <Name>丽红</Name>
    </Student>
    <Student Age="17" Id="3">
      <Name>进李</Name>
    </Student>
    <Student Age="14" Id="4">
      <Name>嗨李</Name>
    </Student>
    <Student Age="15" Id="5">
      <Name>所有</Name>
    </Student>
  </students>
  <Grade>2</Grade>
  <Name>三年</Name>
</Class>
*/

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的项,将生成<Item xsi:nil="true" />,
    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;
}
/*输出
<XClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Items>
    <Teacher>
      <Name>语文老师</Name>
    </Teacher>
    <SubjectTeacher>
      <Name>体育老师</Name>
    </SubjectTeacher>
  </Items>
  <Student xmlns="http://www.cohowinery.com">
    <Teacher xsi:nil="true" />
    <Teacher>
      <Name>语文课代表</Name>
    </Teacher>
  </Student>
  <Items>
    <Item xsi:type="SubjectTeacher">
      <Name>数学老师</Name>
      <Subject>数学</Subject>
    </Item>
    <Item>
      <Name>语文老师</Name>
    </Item>
  </Items>
</XClass>
 */

多维数组
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);
    }
}
/*输出 
 * <XClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Items>
    <Teacher>
      <Name>语文老师</Name>
    </Teacher>
    <SubjectTeacher>
      <Name>体育老师</Name>
    </SubjectTeacher>
  </Items>
  <Student xmlns="http://www.cohowinery.com">
    <Teacher xsi:nil="true" />
    <Teacher>
      <Name>语文课代表</Name>
    </Teacher>
  </Student>
  <Items>
    <Item xsi:type="SubjectTeacher">
      <Name>数学老师</Name>
      <Subject>数学</Subject>
    </Item>
    <Item>
      <Name>语文老师</Name>
    </Item>
  </Items>
</XClass>
 
 */

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
}
/*
输出结果其他代码
 <studentStatus>Single</studentStatus>
其他代码
*/

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;
}
/*输出
    <XClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <XTeacher>
    <Teacher>
      <Name>语文老师</Name>
    </Teacher>
    <Teacher xsi:type="SubjectTeacher">  //注意
      <Name>数学老师</Name>
      <Subject>数学</Subject>
    </Teacher>
  </XTeacher>
</XClass>*/

6、Xml序列化Dictionary
Dictionary 不支持序列化 ,只能自己写,参考地址

XML 反序列化

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

<?xml version="1.0" encoding="utf-8"?>  
<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>MyGroup</GroupName>  
</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。
/// <remarks/>
[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; }
}

/// <remarks/>

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

原文章地址

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值