下面都用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();

        }

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐