模仿protobuf的對象序列化系統


 

下面都用C#語言舉例。其他語言可以參考。

 

 

protobuf的優勢以及缺點

protocolbuffer(以下簡稱PB)是google 的一種數據交換的格式,它獨立於語言,獨立於平臺。源代碼開源。

 

 

優點

1. 速度快,數據小。相同的對象,使用PB比其他類似jsonxml等,數據量更小。

2. 兼容性。PB格式有前向與後向兼容性。數據存儲之後,如果數據協議更改了,老的數據依然可以讀取。而老的協議,也可以讀取新的協議產生的數據。這一點上,jsonxml都可以達到目的,而二進制格式就不行了。但是二進制格式序列化速度更快,數據更小。但是兼容性差很致命。protobuf就相當於二進制格式的改進版。解決了二進制格式兼容性問題。

 

缺點

目前來看,protobuf能滿足大多數需求,但是某些情況下,protobuf不能滿足需求。

1.繼承類。

2.循環引用。

 

這兩點都是protobuf不能完成的。

 

設計的目標:

接近protobuf的性能,同時彌補不足。

 

設計實現:

1.Tag,WireType

 

這兩個字段標記都與protobuf的功能類似。但是,會有一點更改。

 

1.Tag標記除了在字段上標記,同時需要在類上標記,因爲類也需要一個Tag,一個類與其所有父類的Tag必須不同。這樣在反序列化的時候,可以直到一個Field所屬哪個類。

2. WireType表示數據類型。設計WireType的宗旨是,獲取WireType就知道後面數據的長度。

 

這裏命名爲TypeIndex

 

public enum TypeIndex

    {

        None,           //錯誤的類型

        List,              //數組

        Object,        //對象,包含子對象。

        RefObject,  //引用對象

        Map,            //Dictionary

        //Variant,

        String, //字符串

        Fixed8,        //8位數值

        Fixed32,      //32位數值

        Fixed64,      //64位數值

        Custom       //自定義數據

    }

 

 

看一下SkillField方法,從這裏可以看出每個WireType的格式。

object SkipField()

        {

            TypeIndex typeIndex;

            TagType tag;

            ReadFieldHeader(out tag, out typeIndex);

 

            switch (typeIndex)

            {

                case TypeIndex.Fixed32:

                    ms.Position += 4;

                    break;

                case TypeIndex.Fixed64:

                    ms.Position += 8;

                    break;

                case TypeIndex.Fixed8:

                    ms.Position += 1;

                    break;

                case TypeIndex.RefObject:

                    DeserializeRefObject();

                    break;

                case TypeIndex.String:

                    ReadString();

                    break;

                case TypeIndex.List:

                    {

                        int count = reader.ReadInt32();

                        for (int i = 0; i < count; i++)

                        {

                            SkipField();

                        }

                    }

                    break;

                case TypeIndex.Map:

                    {

                        int count = reader.ReadInt32();

                        for (int i = 0; i < count; i++)

                        {

                            SkipField();

                            SkipField();

                        }

                    }

                    break;

                case TypeIndex.Object:

                    {

                        int count = reader.ReadInt32();

                        for (int i = 0; i < count; i++)

                        {

                            SkipField();

                        }

                    }

 

                    break;

                case TypeIndex.Custom:

                    {

                        int size = reader.ReadInt32();

                        ms.Position += size;

                    }

                    break;

 

            }

            return null;

        }

 

 

序列化子類的方法:

一個對象,從基類開始到當前對象的真實類結束,依次序列化每個類的數據。

也同時把類的類型,當最自定義字段(Custom),寫入到對象數據中。這樣在反序列化這個類的時候,就可以反序列化類的運行時類型。

 

序列化循環引用的方法:

保持一個已經序列化的對象的列表,如果當前正在序列化的對象已經被序列化過了,則此對象當做一個引用類型(RefObject)進行序列化,寫入對象在列表的索引。返序列化的時候,這個索引就可以獲取真實對象。

 

關鍵代碼:

(序列化)

protected virtual void SerializeObject(object value, TagType tag)

        {

            //null

            if(value == null)

            {

                WriteFieldHeader(TypeIndex.Object, tag);

                writer.Write(-1);

                return;

            }

 

            //引用類型值

            if (IsReferenceType(value.GetType()))

            {

                if (AsReference(value) || Objects.Contains(value))

                {

                    WriteFieldHeader(TypeIndex.RefObject, tag);

                    SerializeRefObject(value);

                    return;

                }

 

                //添加此引用

                Objects.Add(value);

            }

 

            List<Type> Types = new List<Type>();

            List<TagType> Tags = new List<TagType>();

            Type baseType = value.GetType();

            while(baseType!=null)

            {

                ContractAttribute ca = CompatibleAPI.GetCustomAttribute<ContractAttribute>(baseType);

                if(ca!=null)

                {

                    Types.Add(baseType);

                    Tags.Add(ca.Tag);

                    baseType = baseType.BaseType;

                }

                else

                {

                    break;

                }

            }

 

            if(Types.Count == 0)

            {

                throw new Exception(string.Format("要序列化的類型 {0} 沒有標記序列化屬性Contract", value.GetType().Name));

            }

            WriteFieldHeader(TypeIndex.Object, tag);

            writer.Write(Types.Count+2);    //多二個域寫入類型

 

            //對象類型

            WriteFieldHeader(TypeIndex.Custom, object_type_tag);

            SaveType(value.GetType());

 

            //自定義數據

            WriteFieldHeader(TypeIndex.Custom, object_custom_tag);

            WriteObjectCustomData(value);

 

            for (int i=0;i<Types.Count;i++)

            {

                SerializeObjectTyped(value, Tags[i], Types[i]);

            }

 

           

        }

 

        void SerializeObjectTyped(object value, TagType tag,Type type)

        {

            Dictionary<TagType, FieldInfo> fieldDic = new Dictionary<TagType, FieldInfo>();

            FieldInfo[] fi = type.GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | BindingFlags.NonPublic);

            foreach (var v in fi)

            {

                if(v.DeclaringType == type)

                {

                    MemberAttribute attr = CompatibleAPI.GetCustomAttribute<MemberAttribute>(v);

 

                    if (attr != null && IsSupportedField(v,attr))

                    {

                        fieldDic.Add(attr.Tag, v);

                    }

                }

 

            }

 

            WriteFieldHeader(TypeIndex.Object, tag);

            writer.Write(fieldDic.Count);

 

 

            foreach (var v in fieldDic)

            {

                SerializeCore(v.Value.GetValue(value), v.Value.FieldType, v.Key);

            }

        }

 

        //value type 不能同時爲null

        void SerializeCore(object value,Type type, TagType tag)

        {

            if(value!=null)

            {

                type = value.GetType();

            }

 

            if(type == null)

            {

                throw new ArgumentException("type == null");

            }

 

           

 

            TypeCode tc = Type.GetTypeCode(type);

            TypeIndex ti = GetWireTypeIndex(type);

 

            if (type.IsEnum)

            {

                //int32

                WriteFieldHeader(ti, tag);

                writer.Write((int)value);

                return;

            }

 

            switch (tc)

            {

                case TypeCode.Boolean:

                    WriteFieldHeader(ti, tag);

                    writer.Write((byte)(((bool)value)?1:0));

                    break;

                case TypeCode.Byte:

                    WriteFieldHeader(ti, tag);

                    writer.Write((byte)value);

                    break;

                case TypeCode.SByte:

                    WriteFieldHeader(ti, tag);

                    writer.Write((sbyte)value);

                    break;

                case TypeCode.Char:

                case TypeCode.Int16:

                case TypeCode.UInt16:

                case TypeCode.Int32:

                    WriteFieldHeader(ti, tag);

                    writer.Write((Int32)value);

                    break;

                case TypeCode.UInt32:

                    WriteFieldHeader(ti, tag);

                    writer.Write((UInt32)value);

                    break;

                case TypeCode.Int64:

                    WriteFieldHeader(ti, tag);

                    writer.Write((Int64)value);

                    break;

                case TypeCode.UInt64:

                    WriteFieldHeader(ti, tag);

                    writer.Write((UInt64)value);

                    break;

                case TypeCode.Double:

                    WriteFieldHeader(ti, tag);

                    writer.Write((double)value);

                    break;

                case TypeCode.Single:

                    WriteFieldHeader(ti, tag);

                    writer.Write((float)value);

                    break;

                case TypeCode.String:

                    WriteFieldHeader(ti, tag);

                    WriteString((string)value);

                    break;

                case TypeCode.Object:

                    {

                        if (type.IsGenericType)

                        {

                            if (type.GetGenericTypeDefinition() == typeof(List<>) || type.GetGenericTypeDefinition() == typeof(LinkedList<>))

                            {

                                WriteFieldHeader(ti, tag);

                                if (value == null)

                                {

                                    writer.Write((int)-1);

                                }

                                else

                                {

                                    int count = (int)value.GetType().GetProperty("Count").GetValue(value, null);

                                    Type elementType = value.GetType().GetGenericArguments()[0];

                                    writer.Write(count);

                                    System.Collections.IEnumerable enumerable = value as System.Collections.IEnumerable;

                                    foreach (object o in enumerable)

                                    {

                                        SerializeCore(o, elementType,tag);

                                    }

                                }

                            }

                            else if (type.GetGenericTypeDefinition() == typeof(Dictionary<,>) || type.GetGenericTypeDefinition() == typeof(SortedDictionary<,>))

                            {

                                WriteFieldHeader(ti, tag);

                                if (value != null)

                                {

                                    int count = (int)value.GetType().GetProperty("Count").GetValue(value, null);

                                    Type elementKeyType = value.GetType().GetGenericArguments()[0];

                                    Type elementValueType = value.GetType().GetGenericArguments()[1];

 

                                    writer.Write(count);

                                    System.Collections.IEnumerable enumerable = value as System.Collections.IEnumerable;

                                    foreach (object o in enumerable)

                                    {

                                        var itemKey = o.GetType().GetProperty("Key").GetValue(o, null);

                                        var itemValue = o.GetType().GetProperty("Value").GetValue(o, null);

                                        SerializeCore(itemKey, elementKeyType,1);

                                        SerializeCore(itemValue,elementValueType, 2);

                                    }

                                }

                                else

                                {

                                    writer.Write(-1);

                                }

                            }

                        }

                        else if (type.IsArray)

                        {

                            WriteFieldHeader(ti, tag);

                            if (value==null)

                            {

                                writer.Write((int)-1);

                            }

                            else

                            {

                                Array array = (Array)value;

                                writer.Write(array.Length);

                                //Core.Logger.LogInfo("save array:" + array.Length);

                                for (int i = 0; i < array.Length; i++)

                                    SerializeCore(array.GetValue(i), type.GetElementType(),tag);

                            }

                        }

                        else if (type.IsEnum)

                        {

                            //int32

                            WriteFieldHeader(ti, tag);

                            writer.Write((int)value);

                        }

                        else if ((type.IsValueType && !type.IsPrimitive) || type.IsClass)

                        {

                            SerializeObject(value, tag);

                        }

                    }

                    break;

                default:

                    break;

            }

 

        }

 

 

(反序列化)

protected virtual object DeserializeObject(object value, Type type, TagType tag)

        {

            TypeIndex obj_ti;

            TagType obj_tag;

            ReadFieldHeader(out obj_tag, out obj_ti);

 

            //引用對象

            Asset(TypeIndex.RefObject == obj_ti || TypeIndex.Object == obj_ti);

            if (obj_ti == TypeIndex.RefObject)

            {

                return DeserializeRefObject();

            }

 

           

 

            int count = reader.ReadInt32();

 

            if(count == -1)

            {

                return null;

            }

 

            if(count == 0)

            {

                throw new Exception(string.Format("{0} 對象序列化格式錯誤 count==0 缺失類型", value.GetType().Name));

            }

 

 

            //類型

            {

                TypeIndex field_ti;

                TagType field_tag;

 

                ReadFieldHeader(out field_tag, out field_ti);

 

                Asset(field_ti == TypeIndex.Custom);

                type = ReadType(type);

            }

 

            //自定義數據

            {

                TypeIndex field_ti;

                TagType field_tag;

 

                ReadFieldHeader(out field_tag, out field_ti);

 

                Asset(field_ti == TypeIndex.Custom);

                ReadObjectCustomData(ref value,type,tag);

            }

 

            if (value == null)

            {

                value = Activator.CreateInstance(type);

            }

 

            //對象保存

            if(IsReferenceType(type))

                Objects.Add(value);

 

            Dictionary<int,Type> Types = new Dictionary<int, Type>();

            Type baseType = value.GetType();

            while (baseType != null)

            {

                ContractAttribute ca = CompatibleAPI.GetCustomAttribute<ContractAttribute>(baseType);

                if (ca != null)

                {

                    Types.Add(ca.Tag,baseType);

                    baseType = baseType.BaseType;

                }

                else

                {

                    break;

                }

            }

            if (Types.Count == 0)

            {

                throw new Exception(string.Format("要序列化的類型 {0} 沒有標記序列化屬性Contract", value.GetType().Name));

            }

 

           

            for (int i=0;i<count-2;i++)

            {

                TypeIndex field_ti;

                TagType field_tag;

 

                LookFieldHeader(out field_tag, out field_ti);

 

                if(Types.ContainsKey(field_tag))

                {

                    DeserializeObjectTyped(value, Types[field_tag], field_tag);

                }

                else

                {

                    SkipField();

                }

            }

           

            return value;

        }

 

 

        object DeserializeObjectTyped(object obj, Type type, int tag)

        {

            TypeIndex obj_ti;

            TagType obj_tag;

 

            ReadFieldHeader(out obj_tag, out obj_ti);

            int count = reader.ReadInt32();

 

 

            Dictionary<int, FieldInfo> fieldDic = new Dictionary<int, FieldInfo>();

            FieldInfo[] fi = type.GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | BindingFlags.NonPublic);

            foreach (var v in fi)

            {

                if(v.DeclaringType == type)

                {

                    MemberAttribute attr = CompatibleAPI.GetCustomAttribute<MemberAttribute>(v);

 

                    if (attr != null && IsSupportedField(v, attr))

                    {

                        fieldDic.Add(attr.Tag, v);

                    }

                }

            }

 

            for (int i = 0; i < count; i++)

            {

                TypeIndex field_ti;

                TagType field_tag;

 

                LookFieldHeader(out field_tag, out field_ti);

 

                FieldInfo fieldInfo = null;

                if (fieldDic.TryGetValue(field_tag, out fieldInfo))

                {

                    fieldInfo.SetValue(obj, DeserializeCore(fieldInfo.FieldType, field_tag));

                }

                else

                {

                    SkipField();

                }

            }

 

            return obj;

        }

 

void Asset(bool trueValue)

        {

            if(!trueValue)

            {

                throw new ArgumentException("Asset");

            }

        }

 

        object DeserializeCore(Type type, TagType fieldNumber)

        {

            TypeCode tc = Type.GetTypeCode(type);

 

            if (type.IsEnum)

            {

                TypeIndex ti;

                TagType tag;

                ReadFieldHeader(out tag, out ti);

                Asset(GetWireTypeIndex(type) == ti);

                int v = reader.ReadInt32();

                return Enum.Parse(type, v.ToString());

 

            }

 

 

            switch (tc)

            {

                case TypeCode.Boolean:

                    {

                        TypeIndex ti;

                        TagType tag;

                        ReadFieldHeader(out tag, out ti);

                        Asset(GetWireTypeIndex(type) == ti);

                        return reader.ReadByte()!=0?true:false;

                    }

                case TypeCode.Byte:

                    {

                        TypeIndex ti;

                        TagType tag;

                        ReadFieldHeader(out tag, out ti);

                        Asset(GetWireTypeIndex(type) == ti);

                        return reader.ReadByte();

                    }

                case TypeCode.SByte:

                    {

                        TypeIndex ti;

                        TagType tag;

                        ReadFieldHeader(out tag, out ti);

                        Asset(GetWireTypeIndex(type) == ti);

                        return reader.ReadSByte();

                    }

                case TypeCode.Char:

                case TypeCode.Int16:

                case TypeCode.UInt16:

                case TypeCode.Int32:

                    {

                        TypeIndex ti;

                        TagType tag;

                        ReadFieldHeader(out tag, out ti);

                        Asset(GetWireTypeIndex(type) == ti);

                        return Convert.ChangeType( reader.ReadInt32(),type);

                    }

                case TypeCode.UInt32:

                    {

                        TypeIndex ti;

                        TagType tag;

                        ReadFieldHeader(out tag, out ti);

                        Asset(GetWireTypeIndex(type) == ti);

                        return reader.ReadUInt32();

                    }

                case TypeCode.Int64:

                    {

                        TypeIndex ti;

                        TagType tag;

                        ReadFieldHeader(out tag, out ti);

                        Asset(GetWireTypeIndex(type) == ti);

                        return reader.ReadInt64();

                    }

                case TypeCode.UInt64:

                    {

                        TypeIndex ti;

                        TagType tag;

                        ReadFieldHeader(out tag, out ti);

                        Asset(GetWireTypeIndex(type) == ti);

                        return reader.ReadUInt64();

                    }

                case TypeCode.Double:

                    {

                        TypeIndex ti;

                        TagType tag;

                        ReadFieldHeader(out tag, out ti);

                        Asset(GetWireTypeIndex(type) == ti);

                        return reader.ReadDouble();

                    }

                case TypeCode.Single:

                    {

                        TypeIndex ti;

                        TagType tag;

                        ReadFieldHeader(out tag, out ti);

                        Asset(GetWireTypeIndex(type) == ti);

                        return reader.ReadSingle();

                    }

                case TypeCode.String:

                    {

                        TypeIndex ti;

                        TagType tag;

                        ReadFieldHeader(out tag, out ti);

                        Asset(GetWireTypeIndex(type) == ti);

                        return ReadString();

                    }

                case TypeCode.Object:

                    {

                        if (type.IsGenericType)

                        {

                            if (type.GetGenericTypeDefinition() == typeof(List<>) || type.GetGenericTypeDefinition() == typeof(LinkedList<>))

                            {

                                TypeIndex ti;

                                TagType tag;

                                ReadFieldHeader(out tag, out ti);

                                Asset(GetWireTypeIndex(type) == ti);

                                int count = reader.ReadInt32();

                                if (count == -1)

                                    return null;

 

                                Type elementType = type.GetGenericArguments()[0];

 

                                MethodInfo method = null;

                                if (type.GetGenericTypeDefinition() == typeof(List<>))

                                    method = type.GetMethod("Add", new Type[] { elementType });

                                else

                                    method = type.GetMethod("AddLast", new Type[] { elementType });

 

                                object f = Activator.CreateInstance(type);

 

                                for (int i = 0; i < count; i++)

                                {

                                    object v = DeserializeCore(elementType, tag);

                                    method.Invoke(f, new object[] { v });

                                }

                                return f;

                            }

                            else if (type.GetGenericTypeDefinition() == typeof(Dictionary<,>) || type.GetGenericTypeDefinition() == typeof(SortedDictionary<,>))

                            {

                                TypeIndex ti;

                                TagType tag;

                                ReadFieldHeader(out tag, out ti);

                                Asset(GetWireTypeIndex(type) == ti);

                                int count = reader.ReadInt32();

                                if (count == -1)

                                    return null;

 

                               

                                object f = Activator.CreateInstance(type);

                                Type elementKeyType = f.GetType().GetGenericArguments()[0];

                                Type elementValueType = f.GetType().GetGenericArguments()[1];

 

                                for (int i = 0; i < count; i++)

                                {

                                    object key = DeserializeCore(elementKeyType, 1);

                                    object value = DeserializeCore(elementValueType, 2);

                                    f.GetType().GetMethod("Add").Invoke(f, new object[] { key, value });

                                }

                                return f;

                            }

                        }

                        else if (type.IsArray)

                        {

                            TypeIndex ti;

                            TagType tag;

                            ReadFieldHeader(out tag, out ti);

                            Asset(GetWireTypeIndex(type) == ti);

                            int count = reader.ReadInt32();

                            if (count == -1)

                                return null;

 

                            Type elementType = type.GetElementType();

                            Array v = Array.CreateInstance(elementType, count);

                            //Core.Logger.LogInfo("read array:" + count);

                            for (int i=0;i<count;i++)

                            {

                                v.SetValue(DeserializeCore(elementType,tag), i);

                               

                            }

                            return v;

                        }

                        else if (type.IsEnum)

                        {

                            TypeIndex ti;

                            TagType tag;

                            ReadFieldHeader(out tag, out ti);

                            Asset(GetWireTypeIndex(type) == ti);

                            return Convert.ChangeType( reader.ReadInt32(),type);

                        }

                        else if ((type.IsValueType && !type.IsPrimitive) || type.IsClass)

                        {

                            return DeserializeObject(null, type, fieldNumber);

                        }

                    }

                    break;

                default:

                    break;

            }

 

            return SkipField();

        }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章