继承Dictionary<,>的类出现MessagePack反序列化异常求助
解决MessagePack反序列化
Dictionary<枚举, object>子类的异常问题 这个问题的根源在于MessagePack默认对字典键的序列化逻辑:当你的字典键是枚举类型时,MessagePack会把它序列化为对应的整数值(比如你看到的"21"就是AttributeType枚举某个成员的数值),但反序列化时,默认不会自动将这个整数转换回枚举类型,而是尝试把int类型的键直接塞进Dictionary<AttributeType, object>里,这就触发了类型不匹配的ArgumentException。
下面是几种可行的解决方案,按推荐优先级排序:
方案1:给枚举配置MessagePack的序列化规则
给AttributeType枚举添加特性,明确告诉MessagePack如何处理枚举的序列化和反序列化:
using MessagePack; // 指定枚举按值序列化/反序列化 [MessagePackEnum(EnumSerializationMethod.ByValue)] public enum AttributeType { EventId = 21, // 对应你看到的数值21 // 其他枚举成员... }
然后在序列化/反序列化时,使用包含该配置的选项:
var options = MessagePackSerializerOptions.Standard .WithEnumSerialization(EnumSerializationMethod.ByValue); var @event = new Event { { AttributeType.EventId, Guid.NewGuid() } }; var serializedBytes = MessagePackSerializer.Serialize(@event, options); var deserializedEvent = MessagePackSerializer.Deserialize<Event>(serializedBytes, options);
这个方案最简单直接,不需要编写大量自定义代码,是首选方案。
方案2:自定义Event类的MessagePack格式化器
如果方案1不生效,你可以为Event类编写专属格式化器,手动处理枚举键的转换:
public class EventFormatter : IMessagePackFormatter<Event> { public void Serialize(ref MessagePackWriter writer, Event value, MessagePackSerializerOptions options) { writer.WriteMapHeader(value.Count); foreach (var kvp in value) { // 将枚举键转为整数序列化 writer.Write((int)kvp.Key); // 序列化对应的值 options.Serializer.Serialize(ref writer, kvp.Value); } } public Event Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { var mapCount = reader.ReadMapHeader(); var result = new Event(); for (int i = 0; i < mapCount; i++) { // 读取整数键并转换为枚举类型 var keyInt = reader.ReadInt32(); var key = (AttributeType)keyInt; // 反序列化对应的值 var value = options.Serializer.Deserialize<object>(ref reader); result.Add(key, value); } return result; } }
注册并使用这个格式化器:
var options = MessagePackSerializerOptions.Standard .WithFormatter(new EventFormatter()); var @event = new Event { { AttributeType.EventId, Guid.NewGuid() } }; var serializedBytes = MessagePackSerializer.Serialize(@event, options); var deserializedEvent = MessagePackSerializer.Deserialize<Event>(serializedBytes, options);
方案3:全局统一处理枚举键的字典
如果你的项目中有多个类似的枚举键字典子类,可以通过自定义IFormatterResolver来统一处理:
public class EnumKeyDictionaryResolver : IFormatterResolver { public static readonly EnumKeyDictionaryResolver Instance = new(); public IMessagePackFormatter<T> GetFormatter<T>() { return FormatterCache<T>.Formatter; } private static class FormatterCache<T> { public static readonly IMessagePackFormatter<T> Formatter = (IMessagePackFormatter<T>)CreateFormatter(typeof(T)); } private static object CreateFormatter(Type type) { // 只处理键为枚举的Dictionary类型 if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>) && type.GetGenericArguments()[0].IsEnum) { var formatterType = typeof(EnumKeyDictionaryFormatter<,>).MakeGenericType(type.GetGenericArguments()); return Activator.CreateInstance(formatterType); } return null; } } public class EnumKeyDictionaryFormatter<TKey, TValue> : IMessagePackFormatter<Dictionary<TKey, TValue>> where TKey : Enum { public void Serialize(ref MessagePackWriter writer, Dictionary<TKey, TValue> value, MessagePackSerializerOptions options) { writer.WriteMapHeader(value.Count); foreach (var kvp in value) { writer.Write(Convert.ToInt32(kvp.Key)); options.Serializer.Serialize(ref writer, kvp.Value); } } public Dictionary<TKey, TValue> Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { var mapCount = reader.ReadMapHeader(); var result = new Dictionary<TKey, TValue>(); for (int i = 0; i < mapCount; i++) { var keyInt = reader.ReadInt32(); var key = (TKey)Enum.ToObject(typeof(TKey), keyInt); var value = options.Serializer.Deserialize<TValue>(ref reader); result.Add(key, value); } return result; } }
使用这个全局Resolver:
var options = MessagePackSerializerOptions.Standard .WithResolver(EnumKeyDictionaryResolver.Instance); var @event = new Event { { AttributeType.EventId, Guid.NewGuid() } }; var serializedBytes = MessagePackSerializer.Serialize(@event, options); var deserializedEvent = MessagePackSerializer.Deserialize<Event>(serializedBytes, options);
内容的提问来源于stack exchange,提问作者Hesam Faridmehr
相关产品推荐
相关产品推荐

