本文將說明如何對XML進行序列化與反序列化。(就是將【物件】變成【XML內容】,【XML內容】變成【物件】)
前言
XML雖然可以直接透過XmlDocument進行內容的存取,但為了往後的維護人員與接手開發的同仁著想,若能物件化最好就物件化以方便控管,報復社會的事情沒必要加在同為苦勞人的工程師上。
使用元件
XmlSerializer ( https://msdn.microsoft.com/zh-tw/library/system.xml.serialization.xmlserializer(v=vs.110).aspx )
開始實作
準備一個物件用來對Xml進行存取,這個物件的結構將會影響到Xml內容的結構。
public class XmlDTO
{
public string Element1 { get; set; }
public string Element2 { get; set; }
}
接著序列化就可以得到結果
//using System.Xml.Serialization;
var serilizer = new XmlSerializer(typeof(XmlDTO));
serilizer.Serialize(Console.Out, new XmlDTO
{
Element1 = "Value1",
Element2 = "Value2"
});
可以看到物件內屬性(欄位)預設會被當作XmlElement處理(就是XML的NODE節點),如果說想要設定類別(Attribute)的話要怎麼處理?答案就是加上標籤強制設定。修改一下物件的內容就可以了,非常方便。
public class XmlDTO
{
[XmlAttribute]
public string Element1 { get; set; }
[XmlElement]//Default Type
public string Element2 { get; set; }
}
接著若是一個物件包著一個物件的多層結構,也是如同一般設定物件一樣,直接在放一個物件類別的屬性(欄位)即可。(PS:類別指定的同時也可以一起指定XML對應名稱,用來處理一些特殊符號,像是減號)
public class XmlDTO
{
[XmlAttribute]
public string Element1 { get; set; }
[XmlElement]
public string Element2 { get; set; }
public XmlDTO_Child FirstNode { get; set; }
}
public class XmlDTO_Child
{
[XmlElement("Element1")] //強制指定類別並設定對應XML文檔名稱
public string NodeElement1 { get; set; }
[XmlAttribute]//強制指定類別
public string NodeElement2 { get; set; }
//default mapping
public string NodeElement3 { get; set; }
}
針對多筆的資料,可以設定為List或Array(個人偏好List,操作比較方便)
public class XmlDTO
{
[XmlAttribute]
public string Element1 { get; set; }
[XmlElement]
public string Element2 { get; set; }
public XmlDTO_Child FirstNode { get; set; }
public List<XmlDTO_Child> ChildNode { get; set; }
}
另外由於序列化的目標是Stream,因此在輸出檔案的部分語法也可以很簡單
var serilizer = new XmlSerializer(typeof(XmlDTO));
var XmlDTO = new XmlDTO
{
Element1 = "Value1",
Element2 = "Value2",
FirstNode = new XmlDTO_Child { NodeElement1 = "NodeElement1內容", NodeElement2 = "NodeElement2內容", NodeElement3 = "NodeElement3內容" },
ChildNode = new List<XmlDTO_Child>() { new XmlDTO_Child { NodeElement1 = "NodeElement1Value_1", NodeElement2 = "NodeElement2V_1", NodeElement3 = "NodeElement3V_1" }, new XmlDTO_Child { NodeElement1 = "NodeElement1Value_2", NodeElement2 = "NodeElement2V_2", NodeElement3 = "NodeElement3V_2" } }
};
using (var fileTemp = File.OpenWrite("E://XmlForDemo.xml"))
{
serilizer.Serialize(fileTemp, XmlDTO);
}
若是要從XML檔案中反序列化成為物件呢?這樣就搞定了,真的還挺不錯對吧?
using (var fileTemp = File.OpenRead("E://XmlForDemo.xml"))
{
var dtoResult = serilizer.Deserialize(fileTemp) as XmlDTO;
}
PS:若資源是透過API取得的話,也可以直接使用回傳的Stream進行反序列化。
完整程式碼
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace Core
{
public class XmlSerializerProcess
{
public void Run()
{
var serilizer = new XmlSerializer(typeof(XmlDTO));
//序列化->輸出Console
serilizer.Serialize(Console.Out, new XmlDTO
{
Attr1 = "Attr1V",
Attr2 = "Attr2V",
ListAttr = new List<string> { "A A ", "B B ", " C C" },
Element = "Attr4V",
ListElement = new List<string> { "節點1", "節點2", "節點3" },
FirstNode = new XmlDTO_Child { NodeElement1 = "E1", NodeElement2 = "E2", NodeElement3 = "E3" },
ChildNode = new List<XmlDTO_Child>() { new XmlDTO_Child { NodeElement1 = "NodeElement1Value", NodeElement2 = "NodeElement2V" } }
});
//序列化->檔案儲存
//using (var fileTemp = File.OpenWrite("E://XmlForDemo.xml"))//路徑請自己修改
//{
// serilizer.Serialize(fileTemp, new XmlDTO
// {
// Attr1 = "Attr1V",
// Attr2 = "Attr2V",
// ListAttr = new List<string> { "A A ", "B B ", " C C" },
// Element = "Attr4V",
// ListElement = new List<string> { "節點1", "節點2", "節點3" },
// FirstNode = new XmlDTO_Child { NodeElement1 = "E1", NodeElement2 = "E2", NodeElement3 = "E3" },
// ChildNode = new List<XmlDTO_Child>() { new XmlDTO_Child { NodeElement1 = "NodeElement1Value", NodeElement2 = "NodeElement2V" } }
// });
//}
//反序列化,讀取檔案內容轉變為物件
//using (var fileTemp = File.OpenRead("E://XmlForDemo.xml"))
//{
// var dtoResult = serilizer.Deserialize(fileTemp) as XmlDTO;
//}
}
}
}
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;
namespace Core
{
[XmlRoot("ClassRoot")]
public class XmlDTO
{
[XmlAttribute]
public string Attr1 { get; set; }
[XmlAttribute]
public string Attr2 { get; set; }
[XmlAttribute]
public List<string> ListAttr { get; set; }
public XmlDTO_Child FirstNode { get; set; }
public List<XmlDTO_Child> ChildNode { get; set; }
}
public class XmlDTO_Child
{
[XmlElement("Element1")] //強制指定類別並設定對應XML文檔名稱
public string NodeElement1 { get; set; }
[XmlAttribute]//強制指定類別
public string NodeElement2 { get; set; }
//default mapping
public string NodeElement3 { get; set; }
}
}
注意事項
類別的成員不能有介面(property can not be a interface class),例如你可以使用List當作集合的類別,但是不可以使用IEnumerable
後語
1.關於XML結構可以參考這篇文章:[入門][XML] XML入門系列 (1) : XML 初論 (https://dotblogs.com.tw/johnny/2010/01/25/13303)
(或上W3School看專門的介紹:https://www.w3schools.com/xml/default.asp)
2.網路上很多關於物件設置的要點經過實測後發現....現在大多都不需要了,實測下來需要處理的就是Element / Atrtribute。
3.之前介紹的使用XML自訂WebConfig其實也可以用這個方式設置.,不過使用這個方式的話,資料內容就相會要求嚴謹,錯一個內容就會整份都錯。(反序列化時)