[.net 物件導向程式設計進階] (10) 序列化(Serialization)(二) 通過序列化部落格園文章學習XML的序列化

yubinfeng發表於2015-07-09

[.net 物件導向程式設計進階] (10) 序列化(Serialization)(二) 通過序列化部落格園文章學習XML的序列化

本節導讀:

上節我們介紹了二進位制流的序列化,本節繼續上節內容介紹XML序列化和反序列化。XML作為W3C標準資料傳輸格式,將XML物件化處理,認識和使用XML序列化類XmlSerializer,是.NET物件導向程式設計必須要掌握的知識。

讀前必備:

A.修飾符        [.net 物件導向程式設計基礎]  (8) 基礎中的基礎——修飾符 

B.類和類的例項  [.net 物件導向程式設計基礎]  (9) 類和類的例項

C.類的成員        [.net 物件導向程式設計基礎]  (10) 類的成員(欄位、屬性、方法)

D.陣列與集合   [.net 物件導向程式設計基礎]  (17) 陣列與集合

E.泛型            [.net 物件導向程式設計基礎]  (18) 泛型

F.LINQ使用    [.net 物件導向程式設計基礎] (20) LINQ使用

1. 關於XML

  XML 指可擴充套件標記語言(EXtensible Markup Language)

  XML 是一種標記語言,很類似 HTML

  XML 的設計宗旨是傳輸資料,而非顯示資料

  XML 標籤沒有被預定義。您需要自行定義標籤。

  XML 被設計為具有自我描述性。

  XML 是 W3C 的推薦標準

   關於XML相關的基礎知識,這裡不作為重點贅述,下面入正題。

2. XML序列化

XML序列化的名稱空間是:System.Xml.Serialization

類:XmlSerializer

主要方法:Serialize和Deserialize

下面先來一個簡單有意思的XML序列化,為了讓小夥伴們有興趣看完,我來實現動態獲取部落格園實時資料(這個不是重點,本節重在在於序列化和反序列化),進行序列化。

 首先獲取獲部落格園文章,建立文章實體類,如下:

public class MyBlogs
{
    /// <summary>
    /// 獲取我的部落格園中文章
    /// </summary>
    /// <returns></returns>
    public  static List<MyArticle> GetMyArticle(int count)
    {
        var document = XDocument.Load(
            "http://wcf.open.cnblogs.com/blog/u/yubinfeng/posts/1/" + count
            );
        List<MyArticle> myArticleList = new List<MyArticle>();
        var elements = document.Root.Elements();

        //在進行這個工作之前,我們先獲取我部落格中的文章列表
        var result = elements.Where(m => m.Name.LocalName == "entry").Select(myArticle => new MyArticle
        {
            id = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "id").Value),
            title = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "title").Value,
            published = Convert.ToDateTime(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "published").Value),
            updated = Convert.ToDateTime(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "updated").Value),
            diggs = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "diggs").Value),
            views = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "views").Value),
            comments = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "comments").Value),
            summary = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "summary").Value,
            link = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "link").Attribute("href").Value,
            author = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "author").Elements().SingleOrDefault(x => x.Name.LocalName == "name").Value
        }).OrderByDescending(m=>m.published);
        myArticleList.AddRange(result);
        return myArticleList;
    }
}
/// <summary
/// 我的部落格文章實體類
/// </summary>

public class MyArticle
{
    /// <summary>
    /// 文章編號
    /// </summary>
    public int id { get; set; }
    /// <summary>
    /// 文章標題
    /// </summary>
    public string title { get; set; }
    /// <summary>
    /// 文章摘要
    /// </summary>
    public string summary { get; set; }
    /// <summary>
    /// 釋出時間
    /// </summary>
    public DateTime published { get; set; }
    /// <summary>
    /// 最後更新時間
    /// </summary>
    public DateTime updated { get; set; }
    /// <summary>
    /// URL地址
    /// </summary>
    public string link { get; set; }
    /// <summary>
    /// 推薦數
    /// </summary>
    public int diggs { get; set; }
    /// <summary>
    /// 瀏覽量
    /// </summary>
    public int views { get; set; }

    /// <summary>
    /// 評論數
    /// </summary>
    public int comments { get; set; }
    /// <summary>
    /// 作者
    /// </summary>
    public string author { get; set; }
}

下面實現myArticleList物件序列化XML並輸出到控制檯 

//XML序列化名稱空間:System.Xml.Serialization;
//類:XmlSerializer
//主要方法:Serialize和Deserialize

//部落格資料的物件化(反序列化)及序列化

//(1)獲取我部落格園中最後10篇文章 (實際上這個過程也是反序列化)
List<MyArticle> myArticleList = MyBlogs.GetMyArticle(10);    
//(2)將上面的物件myArticleList序列化XML,並輸出到控制檯
string xmlString = String.Empty;
using (MemoryStream ms = new MemoryStream())
{
               
    XmlSerializer xml = new XmlSerializer(typeof(List<MyArticle>));
    xml.Serialize(ms, myArticleList);
    byte[] arr = ms.ToArray();
    xmlString = Encoding.UTF8.GetString(arr, 0, arr.Length);
    ms.Close();
}
Console.WriteLine(xmlString);

執行結果如下:

 

上面的示例,實現了一個最基本的從物件到XML的序列化。接下來完成上面示例序列化的xmlString文字物件化為List<MyArticle>

3. XML反序列化

Xml反序列化,即將Xml文字轉化為物件的過程。

我們將上面序列化的文字xmlString反序列化為List<MyArticle>物件 

//下面我們剛才輸出的xmlString文字物件化(反序列化)為一個文章List物件
using (StringReader sr = new StringReader(xmlString))
{
    XmlSerializer xml=new XmlSerializer(typeof(List<MyArticle>));
                    ;
    List<MyArticle> myNewArticleList= xml.Deserialize(sr) as List<MyArticle>;

    //遍歷輸出反序化的新物件myNewArticleList
    myNewArticleList.ForEach(m =>
        Console.WriteLine("文章編號:" + m.id + "\n文章標題:" + m.title)                   
    );
    sr.Close();
}

執行結果如下:

  

上面的示例實現了從XML文字反序列化為List<MyArticle>物件的過程。

學會了上面的兩個示例,就掌握了XML序列化和反序列化的基本知識,下面介紹更進一步介紹在序列化過程中的一些細節。

4. XML序列化一些注意事項

(1)要序列化的類必須有預設的構造的建構函式,才能使用XmlSerializer序列化,需要序列化的類都必須有一個無參的建構函式(通過對基礎中類和類的例項學習,我們必須知道類不定義建構函式的情況下,會預設生成一個無引數的建構函式);

(2)索引器、私有欄位或只讀屬性(只讀集合屬性除外)不能被序列化;

(3)不想序列化時:當不想序列化一個屬性時,使用[System.Xml.Serialization.XmlIgnore]標記,能用於屬性;[NonSerializable]應用於屬性無效,能用於類,結構體等;

(4)方法不能被序列化(雖然是廢話,但是還是列舉出來);

(5)列舉變數可序列化為字串,無需用[XmlInclude]

(6)匯出非基本型別物件,都必須用[XmlInclude]事先宣告。該規則遞迴作用到子元素

(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文件讀取再執行一次。

以上十點注意,其中前三點,也就是加黑的這幾點,是重點要知道的。

5. 改變XML序列化的預設值

5.1 更改或刪除XML預設名稱空間

使用 XmlSerializerNamespaces類(System.Xml.Serialization.XmlSerializerNamespaces)。來完成

我們對上面最重得到的物件List<MyArticle>進行XML序列化,並更改XML預設的名稱空間,示例如下:

//更新預設名稱空間
//使用XmlSerializerNamespaces類來完成(System.Xml.Serialization.XmlSerializerNamespaces)
System.Xml.Serialization.XmlSerializerNamespaces XmlSN = new XmlSerializerNamespaces();           
//獲取文章列表物件
List<MyArticle> newArticleList = MyBlogs.GetMyArticle(10);
//更改名稱空間,並輸出
string newXmlString = String.Empty;
XmlSN.Add("MyBlogURL", @"http://www.cnblogs.com/yubinfeng");
using (MemoryStream ms = new MemoryStream())
{
    XmlSerializer xml = new XmlSerializer(typeof(List<MyArticle>));
    xml.Serialize(ms, newArticleList,XmlSN);
    byte[] arr = ms.ToArray();
    newXmlString = Encoding.UTF8.GetString(arr, 0, arr.Length);
    ms.Close();
}
Console.WriteLine(newXmlString);

我們可以和前面預設的輸出結果比較,如下圖:

 

也是可以刪除名稱空間的

System.Xml.Serialization.XmlSerializerNamespaces XmlSN = new XmlSerializerNamespaces();    
XmlSN.Add("","");

當加入一個空的名稱空間時,就可以刪除預設的XML名稱空間。

5.2  更改XML序列化的其他元素

我們先列出XmlWriterSettings可以更改的屬性。

XmlWriterSettings更多設定屬性如下:

成員

說明

CloseOutput

獲取或設定一個值,該值指示在呼叫 Close 方法時,XmlWriter 是否還應該關閉基礎流或 TextWriter

Encoding

獲取或設定要使用的文字編碼的型別。

Indent

獲取或設定一個值,該值指示是否縮排元素。

IndentChars

獲取或設定縮排時要使用的字串。

NamespaceHandling

獲取或設定一個值,該值指示在編寫 XML 內容時,XmlWriter 是否應移除重複的名稱空間宣告。 的預設是輸出程式中出現的所有名稱空間宣告。

NewLineChars

獲取或設定要用於分行符的字串

NewLineHandling

獲取或設定一個值,該值指示是否將輸出中的分行符正常化。

NewLineOnAttributes

獲取或設定一個值,該值指示是否將屬性寫入新行。

OmitXmlDeclaration

獲取或設定一個值指示省略 XML 宣告。

Encoding

獲取或設定要使用的文字編碼的型別。

Reset方法

重置以上屬性

    API官方地址:http://msdn.microsoft.com/zh-cn/library/system.xml.xmlwritersettings(v=vs.110).aspx

   下面我們通過示例。來設定三個屬性:去掉XML宣告,換行縮排,指定字元縮排,示例如下:

先定義一個方法

/// <summary>
/// 設定三個屬性:去掉XML宣告,指定字元縮排
/// </summary>
/// <param name="Obj"></param>
/// <returns></returns>
public static string ObjectToXmlSerializer(Object Obj)
{
    System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings();
            
    settings.OmitXmlDeclaration = true;//去除xml宣告
    settings.Encoding = Encoding.Default;//使用預設編碼
    settings.IndentChars = "--"; //使用指定字元縮排
    settings.Indent = true; //換行縮排
    System.IO.MemoryStream mem = new MemoryStream();
    using (System.Xml.XmlWriter writer = System.Xml.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());
}

    呼叫如下:

//使用 設定 XmlWriterSettings 類的屬性,來更改序列化的XML其他元素
//所在名稱空間:System.Xml.XmlWriterSettings
//設定三個屬性:去掉XML宣告,指定字元縮排
Console.WriteLine(ObjectToXmlSerializer(myArticleList));

輸出結果和之前進行對比,如下圖:

 

6. 本節要點:

(1)瞭解XML的序列化,將物件轉為XML資料格式

(2)瞭解XML的反序列化,將XML文字轉換為物件

(3)瞭解XML序更化的注意事項,主要有以下幾點:

  • 預設類和無引數建構函式類才可以序列化;
  • 索引器、私有欄位或只讀屬性(只讀集合屬性除外)不能被序列化
  • 有一些序列化的標記當不想序列化一個屬性時,使用[System.Xml.Serialization.XmlIgnore]標記,能用於屬性;
  • [NonSerializable]應用於屬性無效,能用於類,結構體等。

(4)改變XML序列化的預設值:

使用 XmlSerializerNamespaces類(System.Xml.Serialization.XmlSerializerNamespaces),更改或刪除XML的名稱空間;

使用 XmlWriterSettings類(System.Xml.XmlWriterSettings)的屬性,來更改XML的明名、縮排等元素。 

     下節我們將對通過實現序列化介面IXmlSerializable來介紹XML序列化以及介紹XML基本操作,並舉例說明,最後會整理一個XML操作的通用類

==============================================================================================  

 返回目錄

 <如果對你有幫助,記得點一下推薦哦,如有有不明白或錯誤之處,請多交流>  

<對本系列文章閱讀有困難的朋友,請先看《.net 物件導向程式設計基礎》>

<轉載宣告:技術需要共享精神,歡迎轉載本部落格中的文章,但請註明版權及URL>

.NET 技術交流群:467189533    .NET 程式設計

==============================================================================================   

相關文章