概念
XML 序列化中的中心類是 XmlSerializer 類,此類中最重要的方法是 Serialize 和 Deserialize 方法 。 XmlSerializer 創建 C# 文件並將其編譯為 .dll 文件,以執行此序列化。 XML 序列化程序生成器工具 (Sgen.exe) 旨在預先生成要與應用程序一起部署的這些序列化程序集,並改進啟動性能。 XmlSerializer 生成的 XML 流符合萬維網聯合會 (W3C) XML 架構定義語言 (XSD) 1.0 建議。 而且,生成的數據類型符合文檔“XML 架構第 2 部分:數據類型”。
基礎知識
在學習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過程中 |
調用默認構造函數 |
不會調用 |
- 針對速度進行了優化(通常比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更多設置屬性如下:
成員 | 說明 |
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的結構,這時候就要用到我們的屬性類了。屬性類提供了很多特性供我們使用,以完成自定義序列化功能。
特性 | 適用對象 | 指定 |
---|---|---|
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 類型的名稱和命名空間。 |
更多更詳細的說明,可以在這里看到: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; } /* 輸出 <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:屬性做節點的文本。<node>節點文本<node>
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<T>列化
數組名稱 [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
不支持序列化 ,只能自己寫
參考:http://45.63.87.5/blog/00159729538804006478dc0ade74c87b77af072c6d47968000
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 ; } }