Serialization V.S. Encoding


Serialization可以看成是把包含相同内容的数据从一种结构 (.NET Object) 转换成另一种结构 (XML) 。要实现在两种不同结构之间的转化,这两种结构之间必须存在一种Mapping。Serialization的是实现由序列化器(Serializer)来负责。而Serializer则利用某种算法(Arithmetic)来提供这种Mapping。我们知道对于一个Managed Type的结构信息——比如它的所有成员的列表,每个成员的Type、访问限制,以及定在每个成员上的属性,作为原数据被存贮在Assembly的原数据表中,这些原数据可以通过反射的机制获得。而XML的结构一般利用XSD来定义。所以 在WCF中的Serialization可以看成是Serializer通过反射的机制分析对象所对应的Type的原数据,从而提供一种算法实现Managed Type的XSD的转化。

很多刚刚接触WCF的人往往不能很好地区分Serialization和Encoding。我们的.NET Object通过Serialization转化成XML Infoset。但是要使我们的数据能够通过网络协议在网络上传递,必须把生成的XML Infoset转化成字节流(Byte Stream)。所以Encoding关注的是XML Infoset到字节流(Byte Stream)这一段转化的过程。在WCF中,有3中不同的方式可供选择:Binary;Text和MTOM(Message Transmit Optimized Mechanism)。Binary具有最好的Performance,Text具有最好的互操作性,MTOM则有利于大量数据的传送。

我们可以这样来理解Serialization和Encoding,Sterilization是基于Service Contract的——而实际上它也是定义在Service Contract中,是放在我们的Code中;而Encoding一般由Binding提供,它是和Service无关的,我们一般在Configuration中根据实际的需要选择我们合适的Encoding。WCF把Serialization和Encoding相互分离是有好处的,Serialization手部署环境的影响相对不大,具有相对的通用性,而Encoding则关系到访问Service的性能以及互操作性等方面,和部署环境紧密相关。比如对于一个在一个Intranet内部使用的系统,往往处于提高Performance考虑,我们一般是使用TCP Transport结合Binary,可能在某一天出现的来自于Internet的潜在的调用,我们不得不改用Http作为Transport,并使用Text Encoding。由于Encoding是可配置的,所以在这种情况下,我们只需要改变Configuration文件就可以了。

DataContractSerializer

Serialization 是通过Serializer来完成的,在WCF中,我们有3种不同的Serializer——DataContractSerializer(定义在System.RunTime.Serializtion namespace中)、XMLSerializer(定义在System.XML.Serialization namespace)和NetDataContractSerializer (定义在System.XML.Serialization namespace) 。他们不同的方式实现.NET Object的Serialization。由于DataContractSerializer和NetDataContractSerializer基本上没有太大的区别,我们只讨论DataContractSerializer和XMLSerializer。其中DataContractSerializer为WCF默认的Serializer,如果没有显式定采用另外一种Serializer,WCF会创建一个DataContractSerializer 序列化NET Object。首先我们来讨论DataContractSerializer采用怎样的一种Mapping方式来把.NET Object转化成XML。我们照例用实验来说明问题。

我们创建两个类DataContractProduct和DataContractOrder用于表示产品和订单两个实体,读者完全可以命知道描述的内容,这里不作特别的介绍。  

 

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Runtime.Serialization;

namespace  Artech.WCFSerialization
{
    [DataContract]
    
public class DataContractProduct
    
{
        
#region Private Fields
        
private Guid _productID;
        
private string _productName;
        
private string _producingArea;
        
private double _unitPrice;
        
#endregion


        
Constructors

        
#region Properties
        [DataMember]
        
public Guid ProductID
        
{
            
get return _productID; }
            
set { _productID = value; }
        }


        [DataMember]
        
public string ProductName
        
{
            
get return _productName; }
            
set { _productName = value; }
        }


        [DataMember]
        
private string ProducingArea
        
{
            
get return _producingArea; }
            
set { _producingArea = value; }
        }


        [DataMember]
        
public double UnitPrice
        
{
            
get return _unitPrice; }
            
set { _unitPrice = value; }
        }


        
#endregion


    }

}

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Runtime.Serialization;

namespace  Artech.WCFSerialization
{
    [DataContract]
    [KnownType(
typeof(DataContractOrder))]
    
public class DataContractOrder
    
{
        
private Guid _orderID;
        
private DateTime _orderDate;
        
private DataContractProduct _product;
        
private int _quantity;

        
#region Constructors
        
public DataContractOrder()
        
{
            
this._orderID = new Guid();
            
this._orderDate = DateTime.MinValue;
            
this._quantity = int.MinValue;

            Console.WriteLine(
"The constructor of DataContractOrder has been invocated!");
        }


        
public DataContractOrder(Guid id, DateTime date, DataContractProduct product, int quantity)
        
{
            
this._orderID = id;
            
this._orderDate = date;
            
this._product = product;
            
this._quantity = quantity;
        }

        
#endregion


        
#region Properties
        [DataMember]
        
public Guid OrderID
        
{
            
get return _orderID; }
            
set { _orderID = value; }
        }

        [DataMember]
        
public DateTime OrderDate
        
{
            
get return _orderDate; }
            
set { _orderDate = value; }
        }

        [DataMember]
        
public DataContractProduct Product
        
{
            
get return _product; }
            
set { _product = value; }
        }

        [DataMember]
        
public int Quantity
        
{
            
get return _quantity; }
            
set { _quantity = value; }
        }

        
#endregion


        
public override string ToString()
        
{
            
return string.Format("ID: {0}\nDate:{1}\nProduct:\n\tID:{2}\n\tName:{3}\n\tProducing Area:{4}\n\tPrice:{5}\nQuantity:{6}",
                
this._orderID, this._orderDate, this._product.ProductID, this._product.ProductName, this._product.ProducingArea, this._product.UnitPrice, this._quantity);
        }

    }

}

使用DataContractSerializer序列化.NET Object。相关的Type必须运用System.Runtime.Serialization. DataContractAttribute, 需要序列化的成员必须运用System.Runtime.Serialization. DataMemberAttribute。为了使我们能够了解DataContract默认的Mapping机制,我们暂时不在DataContractAttribute和DataMemberAttribute设置任何参数。下面我们 来编写具体的Serialization的代码:

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Runtime.Serialization;
using  System.Xml.Serialization;
using  System.IO;
using  System.Xml;
using  System.Diagnostics;

namespace  Artech.WCFSerialization
{
    
class Program
    
{
        
static string _basePath = @"E:\Projects\Artech.WCFSerialization\Artech.WCFSerialization\";

        
static void Main(string[] args)
        
{
            SerializeViaDataContractSerializer();
        }

        
static void SerializeViaDataContractSerializer()
        
{
            DataContractProduct product 
= new DataContractProduct(Guid.NewGuid(), "Dell PC""Xiamen FuJian"4500);
            DataContractOrder order 
= new DataContractOrder(Guid.NewGuid(), DateTime.Today, product, 300);
            
string fileName = _basePath + "Order.DataContractSerializer.xml";
            
using (FileStream fs = new FileStream(fileName, FileMode.Create))
            
{
                DataContractSerializer serializer 
= new DataContractSerializer(typeof(DataContractOrder));
                
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs))
                
{
                    serializer.WriteObject(writer, order);
                }

            }

            Process.Start(fileName);
        }

    }

}

代码很简单,这里不作特别介绍。我们现在只关心生成XML是怎样的结构: 

这里我们总结出以下的Mapping关系:

1.        Root Element为对象的Type Name——DataContractOrder

2.        Type的Namespace会被加到XML根节点的Namespace中http://schemas.datacontract.org/2004/07/Artech.WCFSerialization

3.        对象的所有成员以XML Element的形式而不是以XML Attribute的形式输出。

4.        所以对象在XML的输出顺序是按照字母排序。

5.        所有成员的Elelement 名称为成员名称。

6.        不论成员设置怎样的作用域(public,protected,internal,甚至市Private),

所有运用了DataMemberAttribute的成员均被序列化到XML中——private string ProducingArea。

7.        Type和成员必须运用DataContractAttribute和DataMemberAttribute才能被序列化。

 

上面这些都是默认的Mapping关系,在通常情况下我们用默认的这种Mapping往往不能满足我们的需求,为了把.NET序列化成我们需要的XML 结构(比如我们的XmL必须于我们预先定义的XSD一致),我们可以在这两个Attribute(DataContractAttribute和DataMemberAttribute)制定相关的参数来实现。具体做法如下。

 

1.        Root Element可以通过DataContractAttribute中的Name参数定义。

2.        Namespace可以通过DataContractAttribute中的NameSpace参数定义。

3.        对象的成员只能以XML Element的形式被序列化。

4.        对象成员对应的XML Element在XML出现的位置可以通过DataMemberAttribute的Order参数来定义。

5.        对象成员对应的Element的名称可以通过DataMemberAttribute中的Name定义。

6.        如果不希望某个成员输出到XML中,可以去掉成员对应的DataMemberAttribute Attribute。

此外DataMemberAttribute还有连个额外的参数:

1.         IsRequired:制定该成员为必须的,如果通过工具生成XSD的话,对应的Element的minOccur=“1”

2.        EmitDefaultValue:制定是否输入没有赋值的成员(值为默认值)是否出现在XML中。

基于上面这些,现在我们修改我们的DataContractOrder和DataContractProduct。

 

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Runtime.Serialization;

namespace  Artech.WCFSerialization
{
    [DataContract(Name 
= "product", Namespace = "http://Artech.WCFSerialization/Samples/Product")]
    
public class DataContractProduct
    
{
        
#region Private Fields
        
private Guid _productID;
        
private string _productName;
        
private string _producingArea;
        
private double _unitPrice;
        
#endregion


        
#region Constructors
        
public DataContractProduct()
        
{
            Console.WriteLine(
"The constructor of DataContractProduct has been invocated!");
        }


        
public DataContractProduct(Guid id, string name, string producingArea, double price)
        
{
            
this._productID = id;
            
this._productName = name;
            
this._producingArea = producingArea;
            
this._unitPrice = price;
        }


        
#endregion


        
#region Properties
        [DataMember(Name 
="id",Order = 1)]
        
public Guid ProductID
        
{
            
get return _productID; }
            
set { _productID = value; }
        }


        [DataMember(Name 
= "name", Order = 2)]
        
public string ProductName
        
{
            
get return _productName; }
            
set { _productName = value; }
        }


        [DataMember(Name 
= "producingArea", Order = 3)]
        
internal string ProducingArea
        
{
            
get return _producingArea; }
            
set { _producingArea = value; }
        }


        [DataMember(Name 
= "price", Order = 4)]
        
public double UnitPrice
        
{
            
get return _unitPrice; }
            
set { _unitPrice = value; }
        }


        
#endregion


    }

}

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Runtime.Serialization;

namespace  Artech.WCFSerialization
{
    [DataContract(Name 
="order",Namespace="http://Artech.WCFSerialization/Samples/Order")]
    
public class DataContractOrder
    
{
        
private Guid _orderID;
        
private DateTime _orderDate;
        
private DataContractProduct _product;
        
private int _quantity;

        
#region Constructors
        
public DataContractOrder()
        
{
            
this._orderID = new Guid();
            
this._orderDate = DateTime.MinValue;
            
this._quantity = int.MinValue;

            Console.WriteLine(
"The constructor of DataContractOrder has been invocated!");
        }
    [KnownType(typeof(DataContractOrder))]


        
public DataContractOrder(Guid id, DateTime date, DataContractProduct product, int quantity)
        
{
            
this._orderID = id;
            
this._orderDate = date;
            
this._product = product;
            
this._quantity = quantity;
        }

        
#endregion


        
#region Properties
        [DataMember(Name 
="id",Order =1)]
        
public Guid OrderID
        
{
            
get return _orderID; }
            
set { _orderID = value; }
        }

        [DataMember(Name 
= "date", Order = 2)]
        
public DateTime OrderDate
        
{
            
get return _orderDate; }
            
set { _orderDate = value; }
        }

        [DataMember(Name 
= "product", Order = 3)]
        
public DataContractProduct Product
        
{
            
get return _product; }
            
set { _product = value; }
        }

        [DataMember(Name 
= "quantity", Order = 4)]
        
public int Quantity
        
{
            
get return _quantity; }
            
set { _quantity = value; }
        }

        
#endregion


        
public override string ToString()
        
{
            
return string.Format("ID: {0}\nDate:{1}\nProduct:\n\tID:{2}\n\tName:{3}\n\tProducing Area:{4}\n\tPrice:{5}\nQuantity:{6}",
                
this._orderID, this._orderDate, this._product.ProductID, this._product.ProductName, this._product.ProducingArea, this._product.UnitPrice, this._quantity);
        }

    }

}

再次进行序列化,看看得到的XML是什么样子?

< order  xmlns ="http://Artech.WCFSerialization/Samples/Order"  xmlns:i ="http://www.w3.org/2001/XMLSchema-instance" >
  
< id > 994b42c4-7767-4ed4-bdf8-033e99c00a64 </ id >
  
< date > 2007-03-09T00:00:00+08:00 </ date >
  
< product  xmlns:a ="http://Artech.WCFSerialization/Samples/Product" >
    
< a:id > 137e6c34-3758-413e-8f8a-83f26f78a174 </ a:id >
    
< a:name > Dell PC </ a:name >
    
< a:producingArea > Xiamen FuJian </ a:producingArea >
    
< a:price > 4500 </ a:price >
  
</ product >
  
< quantity > 300 </ quantity >
</ order >

注:对于DataContract Serializer,这里有两点需要我们注意的:

1.         由于Serialization是对数据的不同结构或形态的转化,在转化过程中必须预先知道两种数据相关的原数据(Metadata)。而对于每个.NET对象来说,它的数据结果存放在他所对应的Assembly的原数据表中(Metadata Table),这些原数据表定义的每个定义在该Assembly中的Type的成员定义——包括成员的Type,访问作用域,成员名称,以及运用在成员上的所有Attribute。原则上,只要知道.NET对象对应的Type,我们就可以通过反射(Reflection)的机制分析出该对象的结构。在该例子中,Serializer要序列化DataContractOrder的对象,必须首先知道该对象所属的Type——这个Type通过构造函数传递给Serializer。但是DataContractOrder定义了一个特殊的成员Product,他属于我们的自定义Type:DataContractProduct(这里需要特别指出的是对于.NET的基元类型——Primary Type和一半的常用Type——比如DateTime,Guid等等,Serializer可以自动识别,所以不用特别指定),Serializer是不会识别这个对象的,所以我们需要在定义DataContractOrder的时候运用KnownType Attribute来这个Type。

2.         因为在传统的分布式应用中,我们广泛地采用Serializable Attribute来表明该对象是可以序列化的,DataContract Serializer对这种机制也是支持的。

上面我们讲了Serialization,它的本质就是把.NETObject转化具有一定结构的XML Infoset。被序列化的成的XML Infoset记过Encoding被进一步转化成适合在网络上传递的字节流。当这些字节流从一个Application传递到另一个Application,由于我们的程序的业务逻辑处理的是一个个的.NET对象,所以在目标Application, 会以一个相反的过程把接收到的字节流重构成为和原来一样的.NET Object——目标Application对接收到的字节流记过Decoding转化成XML Infoset,然后通过创建和用于序列化过程一致的Serializer通过Deserialization重建一个.NET Object。所以这就对Serializer提出了要求——它必须为Managed Type的结构和XML的结构提供可逆性的保证——我们把一个.NET Object序列化成一组XML,然后对这组XML进行反序列化重建的对象必须和原来一致。

现在我们在验证这种一致性。在上面的Sample中,我们创建了一个DataContractOrder对象,对它进行序列化并把生成的XML保存的一个文件里面(Order.DataContractSerializer.xml),现在我们都读取这个文件的内容,把它反序列化成DataContractOrder 对象,看它的内容是否和原来一样。下面是Deserialization的逻辑。

static   void  DeserializeViaDataContractSerializer()
        
{
            
string fileName = _basePath + "Order.DataContractSerializer.xml";
            DataContractOrder order;
            
using (FileStream fs = new FileStream(fileName, FileMode.Open))
            
{
                DataContractSerializer serializer 
= new DataContractSerializer(typeof(DataContractOrder));
                
using (XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas()))
                
{
                    order 
= serializer.ReadObject(reader) as DataContractOrder;
                }

            }


            Console.WriteLine(order);
            Console.Read();
        }

调用这个方法,通过在控制台输出DataContractOrder的内容,我们可以确定,通过Deserialization生成的DataContractOrder 对象和原来的对象具有一样的内容。

 通过这里进入Part II。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值