模仿protobuf的对象序列化系统
下面都用C#语言举例。其他语言可以参考。 protobuf的优势以及缺点protocolbuffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。源代码开源。 优点1. 速度快,数据小。相同的对象,使用PB比其他类似json,xml等,数据量更小。2. 兼容性。PB格式有前向与后向兼容性。数据存储之后,如果数据协议更改了,老
下面都用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(); } |
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)