如何在protobuf-net中实现自定义ISerializer处理object类型序列化?
使用protobuf-net自定义序列化器处理含object成员的类型
核心实现思路
当类包含object类型成员时,通过[ProtoContract(Serializer = typeof(YourCustomSerializer))]指定自定义序列化器,序列化时写入目标对象的类型标识(如程序集限定名),反序列化时读取该标识还原对应类型实例,完全规避代理方案的额外内存开销。
自定义序列化器实现步骤
自定义序列化器需实现IProtoSerializer接口,核心实现Write和Read方法,同时通过Features属性声明序列化器特性。
示例代码
- 标记目标类
[ProtoContract(Serializer = typeof(ObjectHolderSerializer))] public class ObjectHolder { public object Content { get; set; } }
- 实现自定义序列化器
public class ObjectHolderSerializer : IProtoSerializer { // 返回当前序列化器处理的目标类型 public Type ExpectedType => typeof(ObjectHolder); // 表示序列化器是否返回新实例(此处返回true,会创建新的ObjectHolder) public bool ReturnsValue => true; // 表示是否需要传入旧实例进行更新(此处不需要,直接创建新实例) public bool RequiresOldValue => false; // 声明序列化器特性,按需组合枚举值 public SerializerFeatures Features => SerializerFeatures.SupportsNull | SerializerFeatures.WireTypeString; public object Read(object existingValue, ProtoReader source) { // 1. 读取类型标识 var typeFullName = source.ReadString(); if (string.IsNullOrEmpty(typeFullName)) { return new ObjectHolder { Content = null }; } // 2. 解析类型并反序列化对象 var targetType = Type.GetType(typeFullName); var content = Serializer.NonGeneric.Deserialize(targetType, source); return new ObjectHolder { Content = content }; } public void Write(object value, ProtoWriter dest) { var holder = (ObjectHolder)value; if (holder.Content == null) { // 写入空标识表示null值 ProtoWriter.WriteString(string.Empty, dest); return; } // 1. 写入类型的程序集限定名 var typeFullName = holder.Content.GetType().AssemblyQualifiedName; ProtoWriter.WriteString(typeFullName, dest); // 2. 序列化实际对象内容 Serializer.NonGeneric.Serialize(dest, holder.Content); } }
SerializerFeatures 详解
SerializerFeatures是标志枚举,用于告知protobuf-net自定义序列化器的行为特性,常用值包括:
SerializerFeatures.SupportsNull:标识序列化器可处理null值,框架会跳过默认的null检查逻辑SerializerFeatures.WireTypeString:表示序列化器输出字符串格式的wire数据,帮助框架正确处理编码解码SerializerFeatures.FixedSize:若序列化后数据长度固定,可设置此标志优化性能SerializerFeatures.CategoryMessage:标识该序列化器处理消息类型(类似普通ProtoContract标记的类),框架按消息规则处理SerializerFeatures.IgnoreConstructor:允许序列化器绕过构造函数直接实例化对象,适用于无参数构造函数缺失的场景
注意事项
- 类型标识建议使用
AssemblyQualifiedName,确保跨程序集类型可正确解析;若担心名称过长,可自定义类型映射表(如用短字符串替代全名)减少序列化体积 - 确保
object成员对应的类型已正确配置protobuf-net特性(如[ProtoContract]、[ProtoMember]),否则无法正常序列化 - 若需进一步优化内存分配,可在
Read/Write方法中复用已有对象(需结合RequiresOldValue = true),或使用内存池化技术处理临时数据
内容的提问来源于stack exchange,提问作者Wolfgang
相关产品推荐
相关产品推荐

