.NET中XML序列化的總結

【題外話】html

之前雖然經常使用.NET中的序列化,可是經常使用的BinaryFormatter,也就是二進制文件的序列化,卻鮮用XML的序列化。對於XML序列化,.NET中一樣提供了一個很是方便的工具XmlSerializer,其能夠很方便的將對象序列化爲XML文件或將XML文件反序列化爲對象。可是XML序列化與二進制序列化卻又很多的區別,在剛開始的時候可能會遇到一些困惑。數組

 

【文章索引】ide

  1. XmlSerializer的做用
  2. 自定義XML結構的映射
  3. 不能序列化的內容
  4. 輸出格式的設置

 

【1、XmlSerializer的做用】工具

.NET提供了很是方便的XML序列化工具XmlSerializer,與二進制序列化工具BinaryFormatter不一樣,XmlSerializer位於System.Xml.Serialization。根據MSDN上對XmlSerializer的說明來看,「XML 序列化是將對象的公共屬性和字段轉換爲序列格式(這裏是指 XML)以便存儲或傳輸的過程。反序列化則是從 XML 輸出中從新建立原始狀態的對象。能夠將序列化視爲將對象的狀態保存到流或緩衝區的方法」,也就是說,咱們能夠直接用XmlSerializer序列化對象中的屬性和字段。ui

須要注意的是,只有public的屬性和字段纔是能夠被序列化的,若是設置的爲internal或者private的屬性或字段都是不能被序列化的。固然,要序列化的對象的類也必須是public的,不然會拋出下列的異常:this

除此以外,要想序列化對象中的字段或者屬性,還須要保證字段和屬性是可讀可寫的。例如,readonly的字段是不能夠序列化的,沒有get或set訪問器的屬性也是不能夠序列化的(固然你能夠選擇在set訪問器裏什麼也不寫,那麼雖然能序列化,可是反序列化的時候就成空的啦)。固然,static和const的字段和屬性也是不會被序列化的,標記爲[Obsolete]的也不會被序列化。此外,除了要求類是public的之外,還須要其有一個無參的構造方法,不然也會拋出異常。編碼

關於XmlSerializer的使用,其實很是簡單,只須要幾行代碼便可實現將一個對象序列化:spa

 1 void SaveToFile(String filePath, Object obj)
 2 {
 3     FileStream fs = null;
 4 
 5     try
 6     {
 7         fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
 8         XmlSerializer xs = new XmlSerializer(obj.GetType());
 9 
10         xs.Serialize(fs, obj);
11     }
12     finally
13     {
14         if (fs != null)
15         {
16             fs.Close();
17         }
18     }
19 }
View Code

或者,反序列化。.net

 1 T LoadFromFile<T>(String filePath)
 2 {
 3     FileStream fs = null;
 4 
 5     try
 6     {
 7         fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
 8         XmlSerializer xs = new XmlSerializer(typeof(T));
 9 
10         return (T)xs.Deserialize(fs);
11     }
12     finally
13     {
14         if (fs != null)
15         {
16             fs.Close();
17         }
18     }
19 }
View Code

 

【2、自定義XML結構的映射】3d

若是按上述的代碼進行序列化,則能夠將對象中的全部公共屬性和字段都序列化進XML文件中。對象中的每一個屬性或字段都會序列化爲一個子元素,若是對象中還有其餘的對象或者數組等還會有更深的子元素。可是有時候咱們可能除了子元素外還須要序列化節點的屬性,或者須要修改映射的名稱等等,那麼咱們就須要對類中的屬性或者字段添加特性(Attributes)了。

與XML序列化相關的常見的特性有:

一、[XmlAttribute]:能夠將指定字段或屬性序列化爲元素的屬性,而不是子元素。除了直接在字段或屬性上方直接寫「[XmlAttribute]」外,還能夠對其傳入參數,例如「[XmlAttribute("identity")]」,能夠改變映射的名稱。例如:

[XmlAttribute("identity")]
public Int32 ID;

類定義及序列化後的結果以下:

public class Student
{
    [XmlAttribute("identity")]
    public Int32 ID;
    public String Name;
}
View Code
<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" identity="1">
  <Name>姓名</Name>
</Student>
View Code

二、[XmlElement]:雖然默認就能夠將字段或屬性序列化爲子元素,可是若是要修改映射的名稱,仍是須要藉助這個特性的。與[XmlAttribute]相似,其也能夠不傳入或傳入參數,當不傳入參數時,與不加該特性相同;當傳入參數時,則能夠修改映射的名稱。例如:

[XmlElement("UserName")]
public String Name;

類定義及序列化後的結果以下:

public class Student
{
    public Int32 ID;
    [XmlElement("UserName")]
    public String Name;
}
View Code
<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ID>1</ID>
  <UserName>姓名</UserName>
</Student>
View Code

三、[XmlText]:除了能序列化爲屬性或者子元素外,還能夠直接做爲該元素的文本內容(InnerText),例若有個類Student,有一個ID咱們但願序列化爲屬性,還有一個Name咱們但願直接做爲Student的內容而不是子元素,那麼咱們就能夠在Name上使用[XmlText]了。例如:

[XmlText]
public String Name;

類定義及序列化後的結果以下:

public class Student
{
    [XmlAttribute]
    public Int32 ID;
    [XmlText]
    public String Name;
}
View Code
<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ID="1">姓名</Student>
View Code

四、[XmlIgnore]:若是一個屬性或字段咱們不但願序列化(好比該屬性是經過其餘字段獲取到的,並無set訪問器等等),那麼咱們能夠經過[XmlIgnore]來讓序列化器來忽略這個屬性或字段。例如:

[XmlIgnore]
public Int32 NameLength { get { return this.Name.Length; } }

類定義及序列化後的結果以下:

public class Student
{
    public Int32 ID;
    public String Name;
    [XmlIgnore]
    public Int32 NameLength { get { return this.Name.Length; } }
}
View Code
<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ID>1</ID>
  <Name>姓名</Name>
</Student>
View Code

五、[XmlArray]:若是須要序列化一個數組或者List等,可是須要修改映射的名稱,那麼咱們就會用到[XmlArray]。須要注意的是,雖然數組等序列化出來的也是一個子元素,可是儘可能不要用[XmlElement],不然數組裏的每個元素至關於對象的直接子元素(除非這個類自己序列化成子元素的就不多或沒有,相似使用[XmlText]的狀況),下邊會給出對比。與[XmlElement]等相相似,若是不設置參數的話,那麼與不添加特性相同;而對其設置參數後,則能夠修改子元素的名稱。例如:

[XmlArray("AllScore")]
public List<Int32> Scores;

類定義及序列化後的結果以下:

public class Student
{
    public Int32 ID;
    public String Name;
    [XmlArray("AllScore")]
    public List<Int32> Scores;
    [XmlElement("FamilyMember")]
    public List<String> FamilyNames;
}
View Code
<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ID>1</ID>
  <Name>姓名</Name>
  <AllScore>
    <int>80</int>
    <int>75</int>
    <int>89</int>
  </AllScore>
  <FamilyMember>父親姓名</FamilyMember>
  <FamilyMember>母親姓名</FamilyMember>
</Student>
View Code

六、[XmlArrayItem]:上述雖然對數組的名稱進行了映射,可是數組裏每個元素的名稱卻沒有定義,因此致使的結果是,全部數組裏元素的名稱都是按照類型名稱來的,好比Int32類型的元素的元素名就是int等等,因此咱們須要使用[XmlArrayItem]特性進行設置,增長上參數之後就能夠映射數組裏元素的名稱了。例如:

[XmlArray("AllScore")]
[XmlArrayItem("Score")]
public List<Int32> Scores;

類定義及序列化後的結果以下:

public class Student
{
    public Int32 ID;
    public String Name;
    [XmlArray("AllScore")]
    [XmlArrayItem("Score")]
    public List<Int32> Scores;
}
View Code
<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ID>1</ID>
  <Name>姓名</Name>
  <AllScore>
    <Score>80</Score>
    <Score>75</Score>
    <Score>89</Score>
  </AllScore>
</Student>
View Code

六、[XmlRoot]:對於類的名稱若是要映射的話,就不能使用上述任何一個特性了,由於若是對類使用的話會提示「它只在「property, indexer, field, param, return」聲明中有效」。這時候咱們就須要[XmlRoot]這個特性,一樣的,對其設置參數,便可以完成對類名稱的映射。例如:

[XmlRoot("StudentInfo")]
public class Student { }

上述幾個特性除了[XmlIgnore]之外,都還支持設置命名參數,以下圖。

例如[XmlElement]、[XmlArray]等能夠設置Order參數,就是能夠強制設置子元素出現位置的前後順序,例如:

public class Student
{
    [XmlElement(Order = 2)]
    public Int32 ID;
    [XmlElement(Order = 1)]
    public String Name;
}

 

【3、不能序列化的內容】

不像BinaryFormatter,XML序列化是有不少東西是不能序列化的,好比衆所周知的Dictionary,咱們其實能夠經過.NET的源代碼來查看到底哪些東西不能序列化爲XML。經過序列化Dictionary拋出異常,能夠找到以下這個類的相關方法,在.NET源代碼的「Source\Net\3.5.50727.3053\DEVDIV\depot\DevDiv\releases\whidbey\netfxsp\ndp\fx\src\Xml\System\Xml\Serialization\Types.cs」目錄下能夠找到。

根據源代碼,能夠發現不能序列化的有如下的類型:

一、繼承IDictionary接口的類型,這個衆所周知了。.NET判斷凡是實現了ICollection接口的都要去System.Xml.Serialization.TypeScope.GetDefaultIndexer()判斷是否繼承了IDictionary接口,若是繼承了則拋出異常。

二、維度大於1的數組,在System.Xml.Serialization.TypeScope.ImportTypeDesc()裏有判斷維度是否大於1,若是維度大於1就拋出異常。

三、ValueType類型,別擔憂,這個不是指全部值類型的不能被序列化,源代碼裏判斷的是「type == typeof(ValueType)」,因此特指ValueType類型的不能被序列化。p.s.我才知道居然能夠建立ValueType類型的變量。

此外,只要知足第一節裏提到的XML序列化的要求的,都能被序列化,整理以下:

一、定義的類或者結構體或者枚舉必須爲public,類或結構體必須有無參的構造方法。好比System.Drawing.Font就沒法實現序列化,由於其沒有無參的構造方法。

二、要序列化的字段或屬性必須爲public,而且不能爲static,標記爲[Obsolete]的不會被序列化。字段不能爲readonlyconst,屬性必須同時有set和get訪問器。好比System.Drawing.Color序列化後不包含任何內容,由於其全部的公有屬性所有隻有get訪問器,沒有set訪問器。

 

【4、輸出格式的設置】

若是對序列化後的XML文件的輸出格式有要求,好比要修改XML文件的編碼、設置XML文件縮進、設置XML的命名空間等等,那麼咱們能夠經過XmlWriter來實現咱們的要求。XmlWriter能夠經過XmlWriter.Create建立,能夠寫入到流、或者直接寫入到文件路徑或者寫入到一個StringBuilder中。

設置XML文件的編碼、縮進等能夠經過建立XmlWriterSettings來設置,例如能夠將縮進字符以及換行字符去除以達到減小文件大小的目的。

 1 XmlWriterSettings settings = new XmlWriterSettings();
 2 settings.Encoding = Encoding.ASCII;
 3 settings.IndentChars = "";
 4 settings.NewLineChars = "";
 5 //或者也能夠這樣
 6 //settings.Indent = false;
 7 //settings.NewLineHandling = NewLineHandling.None;
 8 
 9 XmlWriter xw = XmlWriter.Create(fs, settings);
10 XmlSerializer xs = new XmlSerializer(obj.GetType());
11 xs.Serialize(xw, obj);

而對於設置XML命名空間,則能夠建立XmlSerializerNamespaces,好比能夠添加空的命名空間以取消默認設置的命名空間。

1 XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
2 namespaces.Add(String.Empty, String.Empty);
3 
4 //省略部分代碼
5 
6 xs.Serialize(xw, obj, namespaces);

 

【相關連接】

  1. XmlSerializer 類:http://msdn.microsoft.com/zh-cn/library/system.xml.serialization.xmlserializer.aspx
  2. 在.net中序列化讀寫xml方法的總結:http://www.cnblogs.com/fish-li/archive/2013/05/05/3061816.html
相關文章
相關標籤/搜索