Protobuf.net能否序列化集合?v3版本部分集合反序列化失败问题咨询
问题原因分析
该报错是protobuf-net v3版本的预期行为,和你直接将IReadOnlyCollection<IProtobufSerializable>接口类型作为根对象序列化/反序列化直接相关:
- protobuf-net v2版本对集合接口类型作为根对象的场景做了隐式兼容,会自动默认选用
List<T>作为实际实现类完成序列化、反序列化操作,所以降级到旧版本后运行无异常。 - v3版本重构了类型解析逻辑,收紧了根类型的校验规则:接口类型无法直接确定对应的实例化实现类,且集合作为根对象时的重复项解析逻辑不再被默认纳入辅助类型支持范围,因此抛出
A repeated type was not expected as an aux type错误。
你对问题原因的猜测是正确的,v3版本已经不支持直接序列化/反序列化IReadOnlyCollection<IProtobufSerializable>这类接口集合作为根对象。
可行解决方案
两种方案均可以解决该问题,可根据业务场景选择:
方案1:使用具体集合类型做序列化中转(改造成本最低)
无需额外定义类型,仅修改SerializeItems和DeserializeItems方法,序列化/反序列化时都使用具体的List<T>类型操作,再对外暴露IReadOnlyCollection<T>接口即可,修改后代码如下:
public string SerializeItems<T>(IReadOnlyCollection<T> record) where T : IProtobufSerializable { if (record == null) { throw new ArgumentNullException(nameof(record)); } // 转换为具体List类型再序列化 List<T> serializeList = record.ToList(); using MemoryStream stream = new MemoryStream(); Serializer.SerializeWithLengthPrefix(stream, serializeList, PrefixStyle.Base128); return Convert.ToBase64String(stream.ToArray()); } public IReadOnlyCollection<T> DeserializeItems<T>(string dataInBase64String) where T : IProtobufSerializable { if (dataInBase64String == null) { throw new ArgumentNullException(nameof(dataInBase64String)); } byte[] data = Convert.FromBase64String(dataInBase64String); using MemoryStream stream = new MemoryStream(data); // 直接反序列化为具体List类型,再返回只读集合接口 return Serializer.DeserializeWithLengthPrefix<List<T>>(stream, PrefixStyle.Base128).AsReadOnly(); }
该方案兼容protobuf-net v2和v3全版本,不需要修改现有调用逻辑。
方案2:使用自定义集合包装类(兼容性最强)
如果后续需要扩展集合的附加属性,或者需要兼容更复杂的序列化场景,可以定义带protobuf特性的泛型包装类:
[ProtoContract] public class CollectionWrapper<T> where T : IProtobufSerializable { [ProtoMember(1)] public List<T> Items { get; set; } }
序列化/反序列化时都操作该包装类即可,这种方式的序列化结果结构更稳定,也支持跨语言解析。
内容的提问来源于stack exchange,提问作者Jins Peter
相关产品推荐
相关产品推荐

