You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

继承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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:45:45