8,協議序列化組件NewLife.Serialization
在開發某些需要跟第三方平臺交互的項目時,往往需要解析或者構造符合對方協議要求的數據格式,該操作在.Net中有個很漂亮的名字——序列化!
在實際使用中,XML序列化用得比較多,二進制序列化也不錯,只是可控性很低。當然,對于要序列化指定協議的格式而言,它們就幾乎幫不上忙了。于是有了“協議序列化組件NewLife.Serialization”。
協議序列化類ProtocolFormatter的主旨是實現二進制格式數據和.Net實體數據之間的靈活轉換!
使用上非常簡單,下面通過實現一個簡單的消息類來反序列化手機QQ2008(Mobile)的聊天記錄。
新建一個控制臺項目,引用NewLife.Serialization.dll。加入下面的代碼:
FileStream stream = new FileStream("10000.rec", FileMode.Open); ProtocolFormatter formatter = new ProtocolFormatter(typeof(Message)); formatter.Head.Config.NoHead = true; while (stream.Position < stream.Length) { try { Message msg = new Message(); formatter.Deserialize(stream, msg); Console.WriteLine("{0}({1}) {2} {3}", msg.Name, msg.Number, msg.Time, msg.MsgKind); Console.WriteLine(msg.Content); if (BitConverter.ToString(msg.Data) != "00-00-00-00-00") Console.WriteLine("未知數據:{0}", BitConverter.ToString(msg.Data)); Console.WriteLine(); } catch (EndOfStreamException) { break; } } stream.Close();
第一步實例化一個ProtocolFormatter對象,這里指定了類型為Message;
第二步實例化一個Message對象,這點跟許多組件的反序列化不同,因為有時候外部已經準備好了一個對象,反序列化只需要填充就可以了;
第三步就是序列化,這里傳入第二步實例化的對象。如果這里傳入對象,第一步實例化ProtocolFormatter的時候,就可以不用指定類型了;這里也可以不傳入對象,Deserialize方法內部會實例化一個返回。
下面我們看看Message類:
[ProtocolSerialProperty] public class Message : IProtocolSerializable { #region 屬性 private Int16 _Length; /// <summary>消息長度</summary> public Int16 Length { get { return _Length; } set { _Length = value; } } private String _Content; /// <summary>內容</summary> public String Content { get { return _Content; } set { _Content = value; } } private Int32 _Number; /// <summary>號碼</summary> public Int32 Number { get { return _Number; } set { _Number = value; } } private String _Name; /// <summary>名稱</summary> public String Name { get { return _Name; } set { _Name = value; } } private DateTime _Time; /// <summary>時間</summary> public DateTime Time { get { return _Time; } set { _Time = value; } } private Int16 _Unknown; /// <summary>未知</summary> public Int16 Unknown { get { return _Unknown; } set { _Unknown = value; } } private MsgKinds _MsgKind; /// <summary>消息類型</summary> public MsgKinds MsgKind { get { return _MsgKind; } set { _MsgKind = value; } } private Byte[] _Data; /// <summary>未知數據</summary> public Byte[] Data { get { return _Data; } set { _Data = value; } } #endregion #region 方法 const Char tag = (Char)20; static String FixContent(String content) { content = content.Replace(tag + "A", "[/驚訝]"); content = content.Replace(tag + "N", "[/呲牙]"); content = content.Replace(tag + "M", "[/調皮]"); content = content.Replace(tag + "x", "[/驚恐]"); content = content.Replace(tag + "J", "[/大哭]"); content = content.Replace(tag + "s", "[/難過]"); content = content.Replace(tag + "e", "[/愛心]"); content = content.Replace(tag + "o", "[/強]"); content = content.Replace(tag + "K", "[/尷尬]"); content = content.Replace(tag + "C", "[/色]"); content = content.Replace(tag + "\\", "[/飽]"); content = content.Replace(tag + "E", "[/得意]"); content = content.Replace(tag + "b", "[/玫瑰]"); content = content.Replace(tag + "v", "[/抓狂]"); content = content.Replace(tag.ToString() + (Char)139, "[/可愛]"); content = content.Replace(tag.ToString() + (Char)153, "[/再見]"); content = content.Replace(tag.ToString() + (Char)138, "[/偷笑]"); content = content.Replace(tag.ToString() + (Char)121, "[/流汗]"); content = content.Replace(tag.ToString() + (Char)162, "[/擦汗]"); content = content.Replace(tag.ToString() + (Char)171, "[/委屈]"); content = content.Replace(tag.ToString() + (Char)141, "[/傲慢]"); content = content.Replace(tag.ToString() + (Char)197, "[/獻吻]"); content = content.Replace(tag.ToString() + (Char)146, "[/疑問]"); content = content.Replace(tag.ToString() + (Char)166, "[/壞笑]"); content = content.Replace(tag.ToString() + (Char)149, "[/折磨]"); content = content.Replace(tag.ToString() + (Char)168, "[/右哼哼]"); content = content.Replace(tag.ToString() + (Char)170, "[/鄙視]"); content = content.Replace(tag.ToString() + (Char)172, "[/快哭了]"); content = content.Replace(tag.ToString() + (Char)181, "[/示愛]"); content = content.Replace(tag.ToString() + (Char)140, "[/白眼]"); content = content.Replace(tag.ToString() + (Char)174, "[/愛情]"); content = content.Replace(tag.ToString() + (Char)182, "[/瓢蟲]"); content = content.Replace(tag.ToString() + (Char)161, "[/冷汗]"); //if (content.Contains(tag.ToString())) throw new Exception("未識別!"); return content; } #endregion #region IProtocolSerializable 成員 object IProtocolSerializable.OnCreateInstance(ReadContext context, Type type) { return null; } void IProtocolSerializable.OnDeserialized(ReadContext context) { } bool IProtocolSerializable.OnDeserializing(ReadContext context) { BinaryReader reader = context.Reader; if (context.Node.Name == "Content") { context.Data = ReadString(context.Reader); return false; } else if (context.Node.Name == "Name") { context.Data = ReadString2(context.Reader); return false; } else if (context.Node.Name == "Time") { Int32[] ds = new Int32[7]; Int32 m = 0; for (int i = 0; i < ds.Length; i++) { ds[i] = reader.ReadInt16(); } DateTime dt = new DateTime(ds[0], ds[1], ds[3], ds[4], ds[5], ds[2], DateTimeKind.Utc); context.Data = dt.ToLocalTime(); return false; } else if (context.Node.Name == "Data") { context.Data = context.Reader.ReadBytes(5); return false; } return true; } static String ReadString(BinaryReader reader) { Int32 msglen = reader.ReadInt16(); if (reader.BaseStream.Position > 4) msglen = reader.ReadInt16(); Byte[] buffer = reader.ReadBytes(msglen); String str = Encoding.Unicode.GetString(buffer); str = FixContent(str); return str; } static String ReadString2(BinaryReader reader) { Int32 msglen = reader.ReadInt16(); Byte[] buffer = reader.ReadBytes(msglen * 2); String str = Encoding.Unicode.GetString(buffer); str = FixContent(str); return str; } void IProtocolSerializable.OnSerialized(WriteContext context) { } bool IProtocolSerializable.OnSerializing(WriteContext context) { return true; } #endregion } public enum MsgKinds : short { 系統消息 = 0x110, 帶鏈接系統消息 = 0x116, 新郵件 = 0x117, 請求加好友 = 0x122, 通過加好友請求 = 0x0123 }
Message類主要包含三大部分:
第一是屬性,這點從分析手機QQ2008聊天記錄文件的格式可以得出。我是一邊試一遍猜,猜出來的;
第二是重點。這個類實現了IProtocolSerializable接口,通過OnDeserializing來改變反序列化的行為,某些屬性需要特殊處理的,就在這里處理。
第三部分是處理聊天記錄里面的表情,這個可有可無。
Message類上面有個ProtocolSerialProperty特性,指定反序列化的時候,分析屬性,而不是默認的分析字段。這里指定分析屬性,只是為了方便下面寫代碼判別名稱。
執行效果如下:
協議序列化組件完全通過反射實現,層層深入,所以性能非常差!我們主要用來序列化BT種子以及各種用于網絡傳輸的指令,因為指令對象簡單,性能上還可以接受。
大石頭
新生命開發團隊
2010-09-29 10758

浙公網安備 33010602011771號