XML序列化是將對象的公共屬性和字段轉換爲XML格式,以便存儲或傳輸的過程。反序列化則是從XML輸出中從新建立原始狀態的對象。XML序列化中最主要的類是XmlSerializer類。它的最重要的方法是Serialize和Deserialize方法,它位於System.Xml.Serialization命名空間。數組
在本節開始以前,首先來看一個最簡單的示例:app
namespace 學習測試 { class Program { static void Main(string[] args) { Person p = new Person(1, "劉備", 176); string xmlString = ""; //xml序列化開始 using (MemoryStream ms = new MemoryStream()) { Type t = p.GetType(); XmlSerializer xml = new XmlSerializer(t); xml.Serialize(ms, p); byte[] arr = ms.ToArray(); xmlString = Encoding.UTF8.GetString(arr, 0, arr.Length); ms.Close(); } Console.WriteLine(xmlString); Console.ReadKey(); } } public class Person { //必須定義一個無參數構造函數,不然沒法序列化(固然徹底不寫構造函數也是能夠序列化的,由於有個默認的無參構造函數) public Person() { } public Person(int id, string name, int age) { Id = id; Name = name; //Age = age; } public int Id { get; set; } public string Name { get; set; } //私有字段 private int Age { get; set; } //只讀屬性 private int height; public int Height { get { return height; } } } }
該代碼輸出以下:ide
從以上輸出,咱們能夠得出結論,必需要求無參構造函數,默認的也能夠。但注意當默認的無參構造函數比覆蓋時,要補上一個無參構造函數。另外,私有屬性,只讀屬性是不能被序列化的。函數
更多的注意事項:學習
9.對於沒法XML序列化的對象,可考慮測試
不想序列化時:this
默認構造函數是必須的,由於反序列化本質上使用的是反射,須要默認構造函數來實例化類,若是去掉其中的默認構造函數,則編譯沒有問題,但運行就會報錯。編碼
儘可能不要將比較大的屬性放在默認構造函數初始化,那會致使在反序列化時對列表初始化兩次:默認構造函數中執行一次,反序列化時從XML文檔讀取再執行一次。spa
一般,在XML序列化的過程當中,有不少東西是自動生成的,例如XML命名空間,編碼等等。3d
一、去除默認的命名空間與前綴:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); //第一個參數是前綴,第二個參數是命名空間 //而後在序列化的時候,指定自定義命名空間 xml.Serialize(ms, p, ns);
輸出對比:
固然,這個方法也能夠用於生成你想要的自定義命名空間。
二、去除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()); }
輸出:
三、換行縮進
settings.Indent = true;
當XmlWriterSettings如此設置後,輸出的XML爲:
四、指定縮進字符
settings.IndentChars = "--";
輸出以下:
XmlWriterSettings更多設置屬性以下:
成員 | 說明 |
CloseOutput | 獲取或設置一個值,該值指示在調用 Close 方法時,XmlWriter 是否還應該關閉基礎流或 TextWriter。 |
Encoding | 獲取或設置要使用的文本編碼的類型。 |
Indent | 獲取或設置一個值,該值指示是否縮進元素。 |
IndentChars | 獲取或設置縮進時要使用的字符串。 |
NamespaceHandling | 獲取或設置一個值,該值指示在編寫 XML 內容時,XmlWriter 是否應移除重複的命名空間聲明。 的默認是輸出程序中出現的全部命名空間聲明。 |
NewLineChars | 獲取或設置要用於分行符的字符串 |
NewLineHandling | 獲取或設置一個值,該值指示是否將輸出中的分行符正常化。 |
NewLineOnAttributes | 獲取或設置一個值,該值指示是否將屬性寫入新行。 |
OmitXmlDeclaration | 獲取或設置一個值指示省略 XML 聲明。 |
Encoding | 獲取或設置要使用的文本編碼的類型。 |
Reset方法 | 重置以上屬性 |
http://msdn.microsoft.com/zh-cn/library/system.xml.xmlwritersettings(v=vs.110).aspx
實現IXmlSerializable接口以後,咱們可以自定義類序列化的方式。
該接口包含3個方法:
XmlSchema GetSchema(); void ReadXml(XmlReader reader); void WriteXml(XmlWriter writer);
簡單示例:
namespace 自定義序列化 { class Program { static void Main(string[] args) { Person p = new Person(); p.Id = 1; p.Name = "劉備"; string str = ObjectToXmlSerializer(p); Console.WriteLine(str); Person p1 = ObjectToXmlDESerializer<Person>(str); Console.WriteLine("個人名字是:" + p1.Name); Console.ReadKey(); } //序列化Xml public static string ObjectToXmlSerializer(Object Obj) { string XmlString = ""; XmlWriterSettings settings = new XmlWriterSettings(); //去除xml聲明 //settings.OmitXmlDeclaration = true; settings.Indent = true; settings.Encoding = Encoding.Default; using (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); } XmlString = Encoding.Default.GetString(mem.ToArray()); } return XmlString; } //反序列化 public static T ObjectToXmlDESerializer<T>(string str)where T : class { object obj; using (System.IO.MemoryStream mem = new MemoryStream(Encoding.Default.GetBytes(str))) { using (XmlReader reader = XmlReader.Create(mem)) { XmlSerializer formatter = new XmlSerializer(typeof(T)); obj = formatter.Deserialize(reader); } } return obj as T; } } public class Person { public int Id { get; set; } public string Name { get; set; } } public class PersonSerializer : IXmlSerializable { private Person p; public int Id { get; set; } public string Name { get; set; } #region IXmlSerializable 成員 System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { throw new NotImplementedException(); } //若是這個方法默認則報:XML 文檔(2, 2)中有錯誤。 void IXmlSerializable.ReadXml(XmlReader reader) { reader.ReadElementString("Person"); } void IXmlSerializable.WriteXml(XmlWriter writer) { writer.WriteElementString("Id", Id.ToString()); writer.WriteElementString("Name", Name); } #endregion } }
輸出以下:
咱們都知道,接口是不支持序列化的。下面來作個有用的示例,實現IList<T>的序列化與反序列化:
namespace IList<T>的序列化與反序列化 { class Program { static void Main(string[] args) { Woman w1 = new Woman() { Id = 1, Name = "貂蟬" }; Woman w2 = new Woman() { Id = 2, Name = "西施" }; List<Woman> ListWoman = new List<Woman>(); ListWoman.Add(w1); ListWoman.Add(w2); Person p = new Person(); p.Id = 1; p.Name = "劉備"; p.ListWoman = ListWoman; string str = ObjectToXmlSerializer(p); Console.WriteLine(str); Person p1 = ObjectToXmlDESerializer<Person>(str); Console.WriteLine("個人名字是:" + p1.Name + "個人老婆有:"); foreach (Woman w in p1.ListWoman) { Console.WriteLine(w.Name); } Console.ReadKey(); } //序列化Xml public static string ObjectToXmlSerializer(Object Obj) { string XmlString = ""; XmlWriterSettings settings = new XmlWriterSettings(); //去除xml聲明 //settings.OmitXmlDeclaration = true; settings.Indent = true; settings.Encoding = Encoding.Default; using (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); } XmlString = Encoding.Default.GetString(mem.ToArray()); } return XmlString; } //反序列化 public static T ObjectToXmlDESerializer<T>(string str) where T : class { object obj; using (System.IO.MemoryStream mem = new MemoryStream(Encoding.Default.GetBytes(str))) { using (XmlReader reader = XmlReader.Create(mem)) { XmlSerializer formatter = new XmlSerializer(typeof(T)); obj = formatter.Deserialize(reader); } } return obj as T; } } public class Person : IXmlSerializable { public int Id { get; set; } public string Name { get; set; } public IList<Woman> ListWoman { get; set; } #region IXmlSerializable 成員 System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { throw new NotImplementedException(); } void IXmlSerializable.ReadXml(XmlReader reader) {
//必定要特別注意配對問題,不然很容易反序列化集合出現只可以讀取第一個的狀況
reader.ReadStartElement("Person"); Id = Convert.ToInt32(reader.ReadElementString("Id")); Name = reader.ReadElementString("Name"); //我也不知道爲何,複雜類型只可以另外定義一個,得到值以後再給原來的賦值 List<Woman> ListWoman2 = new List<Woman>(); reader.ReadStartElement("ListWoman"); while (reader.IsStartElement("Woman")) { Woman w = new Woman(); reader.ReadStartElement("Woman"); w.Id = Convert.ToInt32(reader.ReadElementString("Id")); w.Name = reader.ReadElementString("Name"); reader.ReadEndElement(); reader.MoveToContent(); ListWoman2.Add(w); } ListWoman = ListWoman2; reader.ReadEndElement(); reader.ReadEndElement(); } void IXmlSerializable.WriteXml(XmlWriter writer) {
//這裏是不須要WriteStart/End Person的 writer.WriteElementString("Id", Id.ToString()); writer.WriteElementString("Name", Name); //有重載,想設置命名空間,只需在參數加上 writer.WriteStartElement("ListWoman"); foreach (Woman item in ListWoman) { PropertyInfo[] ProArr = item.GetType().GetProperties(); writer.WriteStartElement("Woman"); foreach (PropertyInfo p in ProArr) { writer.WriteElementString(p.Name, p.GetValue(item, null).ToString()); } writer.WriteEndElement(); } writer.WriteEndElement(); } #endregion } public class Woman { public int Id { get; set; } public string Name { get; set; } } }
輸出以下:
以上代碼是可以直接用於序列化數組的,也就是IList<Person>的,下面在貼上兩個序列化與反序列化IList<T>的方法:
//序列化Xml public static string ListToXmlSerializer<T>(IList<T> ListT) { XmlSerializer ser = new XmlSerializer(ListT.GetType()); System.IO.MemoryStream mem = new MemoryStream(); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.OmitXmlDeclaration = false; settings.Encoding = Encoding.UTF8; XmlWriter writer = XmlWriter.Create(mem, settings); ser.Serialize(writer, ListT); writer.Close(); string strtmp = Encoding.UTF8.GetString(mem.ToArray()); return strtmp; } //反序列化Xml public static List<T> XmlToListSerializer<T>(Stream stream) { string XmlPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"\OutLine\" + typeof(T).Name + ".xml"; using (StreamReader sr = new StreamReader(stream, System.Text.Encoding.UTF8)) { XmlSerializer ser = new XmlSerializer(typeof(List<T>)); var listsch = ser.Deserialize(sr); List<T> reses = listsch as List<T>; return reses; } }
下面給出一個序列化與反序列化經過反射的複雜對象的示例:
using System.Linq.Expressions;namespace 控制檯___學習測試 { class Program { static void Main(string[] args) { Woman w1 = new Woman() { Id = 1, Name = "貂蟬" }; Woman w2 = new Woman() { Id = 2, Name = "西施" }; List<Woman> ListWoman1 = new List<Woman>(); ListWoman1.Add(w1); ListWoman1.Add(w2); List<Person> ListPerson = new List<Person>(); Person p1 = new Person() { Id = 1, Name = "劉備", ListWoman = ListWoman1 }; Person p2 = new Person() { Id = 2, Name = "關羽", ListWoman = ListWoman1 }; Person p3 = new Person() { Id = 3, Name = "張飛", ListWoman = ListWoman1 }; ListPerson.Add(p1); ListPerson.Add(p2); ListPerson.Add(p3); string xml = ListToXmlSerializer(ListPerson); Console.WriteLine(xml); MemoryStream mem = new MemoryStream(Encoding.UTF8.GetBytes(xml)); List<Person> ListPerson2 = XmlToListSerializer<Person>(mem); Console.WriteLine(ListPerson2.Count); Console.WriteLine(ListPerson2[2].ListWoman[1].Name); Console.ReadKey(); } //序列化Xml public static string ListToXmlSerializer<T>(IList<T> ListT) { XmlSerializer ser = new XmlSerializer(ListT.GetType()); System.IO.MemoryStream mem = new MemoryStream(); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.OmitXmlDeclaration = true; settings.Encoding = Encoding.UTF8; XmlWriter writer = XmlWriter.Create(mem, settings); ser.Serialize(writer, ListT); writer.Close(); string strtmp = Encoding.UTF8.GetString(mem.ToArray()); File.WriteAllText(@"D:\222.xml", strtmp); return strtmp; } //反序列化Xml public static List<T> XmlToListSerializer<T>(Stream stream) { using (StreamReader sr = new StreamReader(stream, System.Text.Encoding.UTF8)) { XmlSerializer ser = new XmlSerializer(typeof(List<T>)); var listsch = ser.Deserialize(sr); List<T> reses = listsch as List<T>; return reses; } } } public class Person : IXmlSerializable { public int Id { get; set; } public string Name { get; set; } public IList<Woman> ListWoman { get; set; } #region IXmlSerializable 成員 System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { throw new NotImplementedException(); } void IXmlSerializable.ReadXml(XmlReader reader) { //while (reader.Name == "Person") //{ reader.ReadStartElement("Person"); Id = Convert.ToInt32(reader.ReadElementString("Id")); Name = reader.ReadElementString("Name"); List<Woman> newWomans = new List<Woman>(); PropertyInfo[] ProArr = typeof(Woman).GetProperties(); reader.ReadStartElement("ListWoman"); while (reader.IsStartElement("Woman")) { Woman Item2 = new Woman(); reader.ReadStartElement("Woman"); foreach (PropertyInfo p in ProArr) { string str = reader.ReadElementString(p.Name); p.SetValue(Item2, Convert.ChangeType(str, p.PropertyType), null); } reader.ReadEndElement(); reader.MoveToContent(); newWomans.Add(Item2); } ListWoman = newWomans; reader.ReadEndElement(); reader.ReadEndElement(); } void IXmlSerializable.WriteXml(XmlWriter writer) { writer.WriteElementString("Id", Id.ToString()); writer.WriteElementString("Name", Name); writer.WriteStartElement("ListWoman"); foreach (Woman item in ListWoman) { PropertyInfo[] ProArr = item.GetType().GetProperties(); writer.WriteStartElement("Woman"); foreach (PropertyInfo p in ProArr) { if (p.GetValue(item, null) != null) { writer.WriteElementString(p.Name, p.GetValue(item, null).ToString()); } else { writer.WriteElementString(p.Name, ""); } } writer.WriteEndElement(); } writer.WriteEndElement(); } #endregion } public class Woman { public int Id { get; set; } public string Name { get; set; } } }
以上代碼輸出:
特別提示,必定要特別特別注意,ReadStartElement與ReadEndElement的問題,不然很容易出現反序列化集合時只可以讀取第一個的狀況。而對於序列化,若是WriteStartElement與WriteEndElement不匹配,出現的只是XML標籤對不匹配的問題,沒Read的時候那麼坑。
有時,咱們在序列化時想要自定義XML的結構,這時候就要用到咱們的屬性類了。屬性類提供了不少特性供咱們使用,以完成自定義序列化功能。
名稱 | 描述 |
XmlAttribute | 表示一個特性對象的集合,這些對象控制XmlSerializer如何序列化和反序列化對象 |
XmlArrayAttribute | 指定XmlSerializer應將特定的類成員序列化爲XML元素數組 |
XmlArrayItemAttribute | 指定XmlSerializer能夠放置在序列化數組中的派生類型 |
XmlArrayItemAttributes | 表示XmlArrayItemAttribute對象的集合 |
XmlAttributeAttribute | 指定XmlSerializer應將類成員做爲XML特性序列化 |
XmlChoiceIdentifierAttribute | 指定能夠經過使用枚舉來進一步消除成員的歧義 |
XmlElementAttribute | 在XmlSerializer序列化或反序列化包含對象時,指示公共字段或屬性表示XML元素 |
XmlElementAttributes | 表示XmlElementAttribute的集合,XmlSerializer將其用於它重寫序列化類的默認方式 |
XmlEnumAttribute | 控制XmlSerializer如何序列化枚舉成員 |
XmlIgnoreAttribute | 指示XmlSerializer方法不序列化公共字段或公共讀/寫屬性值 |
XmlIncludeAttribute | 容許XmlSerializer在它序列化或反序列化對象時識別類型 |
XmlRootAttribute | 控制視爲XML根元素的屬性目標的XML序列化 |
XmlTextAttribute | 當序列化或反序列化時,想XmlSerializer指示應將此成員做爲XML文本處理 |
XmlTypeAttribute | 控制當屬性目標由XmlSerializer序列化時生成的XML結構 |
XmlAnyAttributeAttribute | 指定成員(返回XmlAttribute對象的數組的字段)能夠包含XML屬性 |
XmlAnyElementAttribute | 指定成員能夠包含對象,該對象表示在序列化或反序列化的對象中沒有相應成員的全部XML元素 |
XmlAnyElementAttributes | 表示XmlAnyElementAttribute對象的集合 |
XmlAttributeEventArgs | 爲UnKnowAttribute提供數據 |
XmlAttributeOverrides | 容許你在使用XmlSerializer序列化或反序列化時重寫屬性、字段和類特性 |
XmlElementEventArgs | 爲UnknownElement事件提供數據 |
XmlNamespaceDeclarationsAttribute | 指定目標屬性、參數、返回值或類成員包含與XML文檔中所用命名空間關聯的前綴 |
XmlNodeEventArgs | 爲UnknownNode時間提供數據 |
XmlSerializer | 將對象序列化到XML文檔中和從XML文檔中反序列化對象,XmlSerializer使你得以控制如何將對象編碼到XML中 |
XmlSerializerNamespaces | 包含XmlSerializer用於在XML文檔實例中生成限定名的XML命名空間和前綴 |
XmlTypeMapping | 包含從一種類型到另外一種類型的映射 |
更多更詳細的說明,能夠在這裏看到:http://msdn.microsoft.com/zh-cn/library/System.Xml.Serialization(v=vs.110).aspx
下面僅僅給出兩個簡單示例:
namespace 學習測試 { [Serializable] public class Person { public Person() { } public int Id { get; set; } public string Name { get; set; } [XmlAttribute(DataType = "string")] public string Content { get; set; } [XmlIgnore] public int Age { get; set; } [XmlArray] [XmlArrayItem("Int32", typeof(Int32))] public IList ListInt { get; set; } } class Program { static void Main(string[] args) { IList list = new ArrayList(); list.Add(1); list.Add(2); list.Add(3); Person p = new Person(); p.Id = 1; p.Name = "劉備"; p.Age = 23; p.Content = "這是一個牛人"; p.ListInt = list; string strXml = ObjectToXmlSerializer(p); Console.WriteLine(strXml); //反序列化IList還有問題 //Person p2 = ObjectToXmlDESerializer<Person>(strXml); //Console.WriteLine(p2.Name); Console.ReadKey(); } //序列化 public static string ObjectToXmlSerializer(Object Obj) { string XmlString = ""; XmlWriterSettings settings = new XmlWriterSettings(); //去除xml聲明 //settings.OmitXmlDeclaration = true; settings.Indent = true; settings.Encoding = Encoding.Default; using (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); } XmlString = Encoding.Default.GetString(mem.ToArray()); } return XmlString; } //反序列化Xml public static T ObjectToXmlDESerializer<T>(string str) where T : class { object obj; using (System.IO.MemoryStream mem = new MemoryStream(Encoding.Default.GetBytes(str))) { using (XmlReader reader = XmlReader.Create(mem)) { XmlSerializer formatter = new XmlSerializer(typeof(T)); obj = formatter.Deserialize(reader); } } return obj as T; } } }
2013/12/27 常遇錯誤記錄:
反序列化錯誤提示:
一、XML 文檔(2, 2)中有錯誤:
報這個錯誤通常是因爲序列化與反序列化的類型不一致:
XmlSerialize.Serialize(@"C:\Person.xml",person); //person 是 Person類的對象 var test = XmlSerialize.DeSerialize(typeof(Person), @"C:\Person.xml");
2014/08/12
二、XmlIgnore與NonSerialized的區別。
一、XmlIgnore能做用於屬性,NonSerialized只做用於字段。
二、XmlIgnore對序列化與反序列化均有效,而NonSerialized隻影響序列化,反序列化無論。(非百分百肯定)