随笔 - 72, 评论 - 94, 访问 - 420978

导航


  • 访问:420978次
  • 积分:768分
  • 排名:第15名
  • 随笔:72篇
  • 评论:94条

随笔分类

随笔档案

相册

最新评论

阅读排行榜

评论排行榜

好饱 阅读(10068) 评论(1)

 

作者:Huan-Lin

 

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(产品),代码如下:

 

[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。代码如下:

 

 

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。

 


评论列表
Nancy
A pllgisnaey rational answer. Good to hear from you.

发表评论
切换编辑模式