C# - XML Serialization and Deserialization(Lưu Trữ Một Object Thành File)

Hôm nay chúng ta sẽ tìm hiểu về XML Serialization and Deserialization(XS&D) - Serialization có thể tạm dịch là "Tuần tự hóa", về cơ bản nó giải quyết được vấn đề như sau: chúng ta có một obj và muốn truyền tải obj đó từ server đến client bằng internet chúng ta sẽ Serialization obj đó thành dạng XML và truyền đi, sau đó ở đầu kia client sẽ Deserialization XML nhận được thành một obj nguyên vẹn như ở server.

XML Serialization and Deserialization

Ngoài ra XML Serialization and Deserialization còn có nhiều ứng dụng khác ví dụ như lưu trữ trạng thái một Application ngay trước khi nó bị Exit và khôi phục trạng thái khi nó Start trở lại hay lưu các thay đổi hệ thống, các setting của người dùng... Thay vì lưu ra file bình thường và đọc chúng khi cần thiết để xậy dụng lại một obj thì chúng ta sử dụng XS&D để tối ưu hóa tốc độ đọc file và building obj.

I> Đặc điểm

XML Serialization and Deserialization có một số điểm cần lưu ý:

  1. XML serialization không chuyển đổi methods, indexers, private fields, hoặc read-only properties (read-only collections)
  2. Các dòng XML được tạo ra bởi các XmlSerializer luôn phù hợp với chuẩn World Wide Web Consortium (www.w3.org) ngôn ngữ định nghĩa lược đồ XML (XSD) 1.0.
  3. Lớp  XmlSerializer có thể tuần tự hóa một đối tượng và tạo ra một định dạng SOAP XML mã hóa. Việc tạo ra XML tuân thủ tài liệu về World Wide Web Consortium mang tên "Simple Object Access Protocol (SOAP) 1.1."

XMLSerialize có thể Serializeting những gì:

  • Public read/write properties và các trường của một public classes.
  • Các class là implement của ICollection hoặc IEnumerable.
  • XmlElement objects.
  • XmlNode objects.
  • DataSet objects.

II> Code mẫu Serializing

* Serializing một DataSet


private void SerializeDataSet(string filename){
XmlSerializer ser = new XmlSerializer(typeof(DataSet));

// Creates a DataSet; adds a table, column, and ten rows.
DataSet ds = new DataSet("myDataSet");
DataTable t = new DataTable("table1");
DataColumn c = new DataColumn("thing");
t.Columns.Add(c);
ds.Tables.Add(t);
DataRow r;
for(int i = 0; i<10;i++){ r = t.NewRow(); r[0] = "Thing " + i; t.Rows.Add(r); } TextWriter writer = new StreamWriter(filename); ser.Serialize(writer, ds); writer.Close(); }





* Serializing XmlElement và XmlNode



private void SerializeElement(string filename){
XmlSerializer ser = new XmlSerializer(typeof(XmlElement));
XmlElement myElement=
new XmlDocument().CreateElement("MyElement", "ns");
myElement.InnerText = "Hello World";
TextWriter writer = new StreamWriter(filename);
ser.Serialize(writer, myElement);
writer.Close();
}

private void SerializeNode(string filename){
XmlSerializer ser = new XmlSerializer(typeof(XmlNode));
XmlNode myNode= new XmlDocument().
CreateNode(XmlNodeType.Element, "MyNode", "ns");
myNode.InnerText = "Hello Node";
TextWriter writer = new StreamWriter(filename);
ser.Serialize(writer, myNode);
writer.Close();
}





* Serializing một Class có chứa các trường public



public class PurchaseOrder
{
public Address MyAddress;
}
public class Address
{
public string FirstName;
}


Kết quả XML



George






* Serializing mảng



public class PurchaseOrder
{
public Item [] ItemsOrders
}

public class Item
{
public string ItemID
public decimal ItemPrice
}


Kết quả XML




aaa111
34.22


bbb222
2.89





* Serializing một class Implements từ Interface ICollection


using System;
using System.IO;
using System.Collections;
using System.Xml.Serialization;

public class Test{
static void Main(){
Test t = new Test();
t.SerializeCollection("coll.xml");
}

private void SerializeCollection(string filename){
Employees Emps = new Employees();
// Note that only the collection is serialized -- not the
// CollectionName or any other public property of the class.
Emps.CollectionName = "Employees";
Employee John100 = new Employee("John", "100xxx");
Emps.Add(John100);
XmlSerializer x = new XmlSerializer(typeof(Employees));
TextWriter writer = new StreamWriter(filename);
x.Serialize(writer, Emps);
}
}
public class Employees:ICollection{
public string CollectionName;
private ArrayList empArray = new ArrayList();

public Employee this[int index]{
get{return (Employee) empArray[index];}
}

public void CopyTo(Array a, int index){
empArray.CopyTo(a, index);
}
public int Count{
get{return empArray.Count;}
}
public object SyncRoot{
get{return this;}
}
public bool IsSynchronized{
get{return false;}
}
public IEnumerator GetEnumerator(){
return empArray.GetEnumerator();
}

public void Add(Employee newEmployee){
empArray.Add(newEmployee);
}
}

public class Employee{
public string EmpName;
public string EmpID;
public Employee(){}
public Employee(string empName, string empID){
EmpName = empName;
EmpID = empID;
}
}


* Tổng hợp


using System;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

// The XmlRootAttribute allows you to set an alternate name
// (PurchaseOrder) for the XML element and its namespace. By
// default, the XmlSerializer uses the class name. The attribute
// also allows you to set the XML namespace for the element. Lastly,
// the attribute sets the IsNullable property, which specifies whether
// the xsi:null attribute appears if the class instance is set to
// a null reference.
[XmlRootAttribute("PurchaseOrder", Namespace="http://www.cpandl.com",
IsNullable = false)]
public class PurchaseOrder
{
public Address ShipTo;
public string OrderDate;
// The XmlArrayAttribute changes the XML element name
// from the default of "OrderedItems" to "Items".
[XmlArrayAttribute("Items")]
public OrderedItem[] OrderedItems;
public decimal SubTotal;
public decimal ShipCost;
public decimal TotalCost;
}

public class Address
{
// The XmlAttribute instructs the XmlSerializer to serialize the
// Name field as an XML attribute instead of an XML element (the
// default behavior).
[XmlAttribute]
public string Name;
public string Line1;

// Setting the IsNullable property to false instructs the
// XmlSerializer that the XML attribute will not appear if
// the City field is set to a null reference.
[XmlElementAttribute(IsNullable = false)]
public string City;
public string State;
public string Zip;
}

public class OrderedItem
{
public string ItemName;
public string Description;
public decimal UnitPrice;
public int Quantity;
public decimal LineTotal;

// Calculate is a custom method that calculates the price per item
// and stores the value in a field.
public void Calculate()
{
LineTotal = UnitPrice * Quantity;
}
}

public class Test
{
public static void Main()
{
// Read and write purchase orders.
Test t = new Test();
t.CreatePO("po.xml");
t.ReadPO("po.xml");
}

private void CreatePO(string filename)
{
// Creates an instance of the XmlSerializer class;
// specifies the type of object to serialize.
XmlSerializer serializer =
new XmlSerializer(typeof(PurchaseOrder));
TextWriter writer = new StreamWriter(filename);
PurchaseOrder po=new PurchaseOrder();

// Creates an address to ship and bill to.
Address billAddress = new Address();
billAddress.Name = "Teresa Atkinson";
billAddress.Line1 = "1 Main St.";
billAddress.City = "AnyTown";
billAddress.State = "WA";
billAddress.Zip = "00000";
// Sets ShipTo and BillTo to the same addressee.
po.ShipTo = billAddress;
po.OrderDate = System.DateTime.Now.ToLongDateString();

// Creates an OrderedItem.
OrderedItem i1 = new OrderedItem();
i1.ItemName = "Widget S";
i1.Description = "Small widget";
i1.UnitPrice = (decimal) 5.23;
i1.Quantity = 3;
i1.Calculate();

// Inserts the item into the array.
OrderedItem [] items = {i1};
po.OrderedItems = items;
// Calculate the total cost.
decimal subTotal = new decimal();
foreach(OrderedItem oi in items)
{
subTotal += oi.LineTotal;
}
po.SubTotal = subTotal;
po.ShipCost = (decimal) 12.51;
po.TotalCost = po.SubTotal + po.ShipCost;
// Serializes the purchase order, and closes the TextWriter.
serializer.Serialize(writer, po);
writer.Close();
}

protected void ReadPO(string filename)
{
// Creates an instance of the XmlSerializer class;
// specifies the type of object to be deserialized.
XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder));
// If the XML document has been altered with unknown
// nodes or attributes, handles them with the
// UnknownNode and UnknownAttribute events.
serializer.UnknownNode+= new
XmlNodeEventHandler(serializer_UnknownNode);
serializer.UnknownAttribute+= new
XmlAttributeEventHandler(serializer_UnknownAttribute);

// A FileStream is needed to read the XML document.
FileStream fs = new FileStream(filename, FileMode.Open);
// Declares an object variable of the type to be deserialized.
PurchaseOrder po;
// Uses the Deserialize method to restore the object's state
// with data from the XML document. */
po = (PurchaseOrder) serializer.Deserialize(fs);
// Reads the order date.
Console.WriteLine ("OrderDate: " + po.OrderDate);

// Reads the shipping address.
Address shipTo = po.ShipTo;
ReadAddress(shipTo, "Ship To:");
// Reads the list of ordered items.
OrderedItem [] items = po.OrderedItems;
Console.WriteLine("Items to be shipped:");
foreach(OrderedItem oi in items)
{
Console.WriteLine("\t"+
oi.ItemName + "\t" +
oi.Description + "\t" +
oi.UnitPrice + "\t" +
oi.Quantity + "\t" +
oi.LineTotal);
}
// Reads the subtotal, shipping cost, and total cost.
Console.WriteLine(
"\n\t\t\t\t\t Subtotal\t" + po.SubTotal +
"\n\t\t\t\t\t Shipping\t" + po.ShipCost +
"\n\t\t\t\t\t Total\t\t" + po.TotalCost
);
}

protected void ReadAddress(Address a, string label)
{
// Reads the fields of the Address.
Console.WriteLine(label);
Console.Write("\t"+
a.Name +"\n\t" +
a.Line1 +"\n\t" +
a.City +"\t" +
a.State +"\n\t" +
a.Zip +"\n");
}

protected void serializer_UnknownNode
(object sender, XmlNodeEventArgs e)
{
Console.WriteLine("Unknown Node:" + e.Name + "\t" + e.Text);
}

protected void serializer_UnknownAttribute
(object sender, XmlAttributeEventArgs e)
{
System.Xml.XmlAttribute attr = e.Attr;
Console.WriteLine("Unknown attribute " +
attr.Name + "='" + attr.Value + "'");
}
}

Kết quả XML




1 Main St.
AnyTown
WA
00000

Wednesday, June 27, 2001


Widget S
Small widget
5.23
3
15.69


15.69
12.51
28.2



II> XML Serialization sử dụng Attributes

* XmlArrayAttribute

Không sử dụng XmlArrayAttribute

public class Group{
public Employee[] Employees;
}
public class Employee{
public string Name;
}


Kết quả XML





Haley





Sử dụng XmlArrayAttribute thay đổi tên một XML element


public class Group{
[XmlArray("TeamMembers")]
public Employee[] Employees;
}


Kết quả XML




Haley







* XmlArrayItemAttribute



public class Group{
[XmlArrayItem("MemberName")]
public Employee[] Employees;
}


Kết quả XML




Haley



hay

public class Group{
[XmlArrayItem(Type = typeof(Employee)),
XmlArrayItem(Type = typeof(Manager))]
public Employee[] Employees;
}
public class Employee{
public string Name;
}
public class Manager:Employee{
public int Level;
}

Kết quả XML




Haley


Ann
3








* XmlElementAttribute



public class Group{
[XmlElement]
public Employee[] Employees;
}

Kết quả XML



Haley


Noriko


Marco




IV> Deserializing XML Data

Cách đọc ngược xml build thành object


using System;
using System.IO;
using System.Xml.Serialization;

namespace XMLTest1
{
public class Test
{
public String value1;
public String value2;
}

class Program
{
static void Main(string[] args)
{
String xData = "Value 1Value 2";
XmlSerializer x = new XmlSerializer(typeof(Test));
Test myTest = (Test)x.Deserialize(new StringReader(xData)/* đọc từ string hoặc từ file*/);
Console.WriteLine("V1: " + myTest.value1);
Console.WriteLine("V2: " + myTest.value2);
Console.ReadKey();
}
}
}


(tham khảo nguồn Microsoft)

Chúc các bạn thành công!

Phạm Tuân


Chúc các bạn thành công!
PHẠM TUÂN