下面都用C#語言舉例。其他語言可以參考。
protobuf的優勢以及缺點
protocolbuffer(以下簡稱PB)是google 的一種數據交換的格式,它獨立於語言,獨立於平臺。源代碼開源。
優點
1. 速度快,數據小。相同的對象,使用PB比其他類似json,xml等,數據量更小。
2. 兼容性。PB格式有前向與後向兼容性。數據存儲之後,如果數據協議更改了,老的數據依然可以讀取。而老的協議,也可以讀取新的協議產生的數據。這一點上,json與xml都可以達到目的,而二進制格式就不行了。但是二進制格式序列化速度更快,數據更小。但是兼容性差很致命。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(); } |