如何使用DataContractSerializer序列化对象
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用DataContractSerializer序列化对象相关的知识,希望对你有一定的参考价值。
DataContractJsonSerializer 类别是 .NET Framework 3.5 的新成员,它可以用来将对象序列化成 JSON 格式的字串,或者执行反序列化,也就是根据 JSON 格式的字串内容来建立对象,并还原对象的状态。DataContractJsonSerializer 的用法很简单,网络上已经可以查到不少范例和教学文章,例如 Chris Pietschmann 的 .NET 3.5: JSON Serialization using the DataContractJsonSerializer。
Pietschmann 的文章里有提供一个 JsonHelper 类别,在撰写序列化/反序列化程式时挺方便的。我做了点小修改,把里面的
Encoding.Default 改为 Encoding.Utf8,然后写个小程式测试一下,以确认
DataContractJsonSerializer 是否符合我的需求,主要是:
支持深层序列化(巢状属性)
新旧版本的容错(稍后说明)
支持自订类型和常用的 .NET 类型,包括:string、int、double、DateTime、bool 等。
为了测试至少三层的巢状序列化,我写了几个简单的类别,包括 Order(订单)、OrderItem(订单细项)、Product(产品),代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
[DataContract]
public class Order
private List<OrderItem> items;
public Order()
public Order(int id, DateTime orderDate, bool useCoupon)
this.ID = id;
this.OrderDate = orderDate;
this.UseCoupon = useCoupon;
this.items = new List<OrderItem>();
[DataMember]
public int ID get; set;
[DataMember]
public DateTime OrderDate get; set;
[DataMember]
public bool UseCoupon get; set;
[DataMember]
public List<OrderItem> Items
get return items;
set items = value;
[DataContract]
public class OrderItem
public OrderItem(Product product, int count)
this.Product = product;
this.Count = count;
[DataMember]
public Product Product get; set;
[DataMember]
public int Count get; set;
[DataContract]
public class Product
public Product(string name, double price)
this.Name = name;
this.Price = price;
[DataMember]
public string Name get; set;
[DataMember]
public double Price get; set;
要点:欲序列化的类别需套用 DataContract attribute;同时,欲序列化的类别属性还必须套用 DataMember attribute,否则该属性不会参与序列化/反序列化过程。
接著写一个 Windows Forms 程式来观察序列化和反序列化的结果:在 form 上面放两个 Button 和一个 TextBox,两个按钮分别执行序列化和反序列化的动作,结果均输出至 TextBox。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void btnSerialize_Click(object sender, EventArgs e)
Order order = new Order(1, DateTime.Now, true);
order.Items.Add(new OrderItem(new Product("脚踏车", 3000), 2));
order.Items.Add(new OrderItem(new Product("鼠标垫", 35.6), 10));
string jsonStr = JsonHelper.Serialize<Order>(order);
textBox1.Text = jsonStr;
private void btnDeserialize_Click(object sender, EventArgs e)
Order order = JsonHelper.Deserialize<Order>(textBox1.Text);
textBox1.AppendText("\\r\\n\\r\\n反序列化结果:");
textBox1.AppendText("\\r\\nOrderID: " + order.ID.ToString());
textBox1.AppendText("\\r\\nOrderDate: " + order.OrderDate.ToString("yyyy/MM/dd HH:mm:ss"));
textBox1.AppendText("\\r\\nUseCoupon: " + order.UseCoupon.ToString());
foreach (OrderItem item in order.Items)
textBox1.AppendText("\\r\\n==========================");
textBox1.AppendText("\\r\\nProduct name: " + item.Product.Name);
textBox1.AppendText("\\r\\nPrice: " + item.Product.Price.ToString());
textBox1.AppendText("\\r\\nCount: " + item.Count.ToString());
此范例代码须加入组件引用:System.Runtime.Serialization 和 System.ServiceModel.Web。JsonHelper 类的原始代码请参考前面提到的 Chris Pietschmann 的文章。
执行结果如下图:
点「Serialize」按钮时所产生的序列化结果是:
"ID":1,"Items":
["Count":2,"Product":"Name":"脚踏车","Price":3000,
"Count":10,"Product":"Name":"鼠标
垫","Price":35.6],"OrderDate":"\\/Date(1261443680236+0800)\\/","UseCoupon":true
这是标准的 JSON 格式字串。要注意的是,如果你在类别上套用的 attribute 是 [Serializable] 而不是 [DataContract],程式虽然也能执行,但输出的序列化字串格式会不太一样,变成这样:
"<ID>k__BackingField":1,"k__BackingField":"\\/Date(1261443890904+0800)
\\/","k__BackingField":true,"items":["Count":2,"Product":"Name":"脚踏
车","Price":3000,"Count":10,"Product":"Name":"鼠标垫","Price":35.6]
小结
我有个应用程式是用 BinaryFormatter
来序列化多层巢状的对象结构,并将对象状态储存至外部档案。后来发现那些要序列化的类别在程式改版时偶尔会有增加或删除属性的情形,导致新旧版本的的档案
不相容(反序列化时会出现 exception),而且二进位格式的档案就像个黑盒子,即使想动点手脚来转换新旧档案的格式都很困难。
由於使用 JSON 文字格式,DataContractJsonSerializer
在执行反序列化时的「容错」程度比二进位格式好很多。我试著把范例程式输出的 JOSN
字串内容稍微修改,例如,将「,"Price":3000」整个栏位资料删除,或者调动两个栏位的顺序,然后再执行
Deserialize。结果都很顺利,那些消失的栏位所对应的属性,会被设定成该属性的类型的预设值(例如 int 就是
0)。这表示程式改版时可以比较放心地增删类别的属性,不至於为了跟旧版的资料格式相容而绑手绑脚。
若要使用文字格式,还有 XmlSerializer 可以选择,但它有些限制(例如:无法处理有实作 IDictionary 的类别),而且就产生的资料大小而言,JSON 也比 XML 精简些。
所以结论就是:改用 DataContractJsonSerializer。 参考技术A 下面的这段代码演示了如何使用DataContractSerializer序列化程序序列化对象。
代码中首先使用LINQ to SQL从数据库中读取编号为ALFKI的客户的记录,并返回一个Customer实体类对象。
然后使用DataContractSerializer序列化对象,并将生成的XML打印到控制台上。
此示例代码使用DataContractSerializer.WriterObject()这个方法。
[c-sharp] view plain copy
NorthwindDataContext db = new NorthwindDataContext(@"C:/LINQ/Northwind.mdf");
Customer CustomerObject =
db.Customers.Where(C => C.CustomerID == "ALFKI").Single();
DataContractSerializer Serializer = new DataContractSerializer(typeof(Customer));
StringBuilder SBuilder = new StringBuilder();
XmlWriter Writer = XmlWriter.Create(SBuilder);
Serializer.WriteObject(Writer, CustomerObject);
Writer.Close();
string Xml = SBuilder.ToString();
Console.WriteLine(Xml);
C#序列化总结
贴一下自己序列化的代码:
public class XMLUtil { /// <summary> /// XML & Datacontract Serialize & Deserialize Helper /// </summary> /// <typeparam name="T"></typeparam> /// <param name="serialObject"></param> /// <returns></returns> public static string Serializer<T>(T serialObject) where T : class { string result = string.Empty; using (MemoryStream mem = new MemoryStream()) { using (XmlTextWriter writer = new XmlTextWriter(mem, Encoding.UTF8)) { System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(T)); ser.Serialize(writer, serialObject); result = Encoding.UTF8.GetString(mem.ToArray()); } } return result; } public static T Deserialize<T>(string str) where T : class { T result = null; using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(str))) { using (StreamReader streamReader = new StreamReader(memoryStream)) { System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T)); result = (T)xmlSerializer.Deserialize(memoryStream); } } return result; } }
上面的写法持续序列化不会有内存溢出的性能问题,之前一直被告知直接引用公司某位老鸟封装好的dll来序列化,后来发现了老是出现内存溢出,贴一下它的错误写法,仅供吸取教训:
public class XMLUtil { /// <summary> /// XML & Datacontract Serialize & Deserialize Helper /// </summary> /// <typeparam name="T"></typeparam> /// <param name="serialObject"></param> /// <returns></returns> public static string Serializer<T>(T serialObject) where T : class { //try //{ XmlSerializer ser = new XmlSerializer(typeof(T)); System.IO.MemoryStream mem = new MemoryStream(); XmlTextWriter writer = new XmlTextWriter(mem, Encoding.UTF8); ser.Serialize(writer, serialObject); writer.Close(); return Encoding.UTF8.GetString(mem.ToArray()); //} //catch (Exception ex) //{ // return null; //} } public static T Deserialize<T>(string str) where T : class { //try //{ XmlSerializer mySerializer = new XmlSerializer(typeof(T)); StreamReader mem2 = new StreamReader( new MemoryStream(System.Text.Encoding.UTF8.GetBytes(str)), System.Text.Encoding.UTF8); return (T)mySerializer.Deserialize(mem2); //} //catch (Exception) //{ // return null; //} } //public static string Json_SerializeObject(object value) //{ // return Newtonsoft.Json.JsonConvert.SerializeObject(value); //} //public static object Json_DeserializeObject(string value) //{ // return Newtonsoft.Json.JsonConvert.DeserializeObject(value); //} //public static T Json_DeserializeObject<T>(string value) //{ // return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(value); //} }
哎,老鸟赶时间的时候写代码都这么随意吗?看到被注释掉的try catch我猜测他曾经也觉得这里有问题,不过还是没发现吧。直接放内存里,流都不释放啊。。。
以上是关于如何使用DataContractSerializer序列化对象的主要内容,如果未能解决你的问题,请参考以下文章
如何在自动布局中使用约束标识符以及如何使用标识符更改约束? [迅速]