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

如何反序列化以枚举元组为键的Dictionary属性?

解决Dictionary<Tuple<Color, Shape>, Stuff>的JSON反序列化问题

核心问题是Json.NET默认将Tuple<Color, Shape>序列化为类似"(Red, Round)"的字符串,反序列化时无法自动将该字符串解析回Tuple类型。解决办法是自定义Json转换器,控制Tuple键的序列化格式和反序列化逻辑,无需修改第三方类或原有业务模型。

1. 自定义Tuple转换器

创建继承自JsonConverter<Tuple<Color, Shape>>的转换器,实现自定义的序列化和反序列化逻辑:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;

public class TupleColorShapeConverter : JsonConverter<Tuple<Color, Shape>>
{
    // 使用竖线作为枚举值的分隔符,可根据需求调整
    private const string Separator = "|";

    public override void WriteJson(JsonWriter writer, Tuple<Color, Shape> value, JsonSerializer serializer)
    {
        // 将Tuple序列化为"Color|Shape"格式的字符串,避免默认的带括号格式
        var keyString = $"{value.Item1}{Separator}{value.Item2}";
        writer.WriteValue(keyString);
    }

    public override Tuple<Color, Shape> ReadJson(JsonReader reader, Type objectType, Tuple<Color, Shape> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.TokenType != JsonToken.String)
            throw new JsonSerializationException("Tuple键必须是字符串格式");

        var keyString = reader.Value.ToString();
        var parts = keyString.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries);
        
        if (parts.Length != 2)
            throw new JsonSerializationException($"无效的键格式:{keyString},请使用'Color|Shape'格式");

        // 支持忽略大小写解析枚举,提升鲁棒性
        if (!Enum.TryParse(parts[0], true, out Color color))
            throw new JsonSerializationException($"无效的Color枚举值:{parts[0]}");
        if (!Enum.TryParse(parts[1], true, out Shape shape))
            throw new JsonSerializationException($"无效的Shape枚举值:{parts[1]}");

        return Tuple.Create(color, shape);
    }
}

2. 使用转换器进行序列化/反序列化

方式一:显式指定序列化配置

在序列化和反序列化时,将转换器传入JsonSerializerSettings:

// 初始化测试数据
var shelvingUnit = new ShelvingUnit();
shelvingUnit.Bins.Add(Tuple.Create(Color.Red, Shape.Round), new Stuff { Purpose = "Storage", NumberOfThings = 10 });
shelvingUnit.Classifiers.Add(Tuple.Create(Color.Blue, Shape.Rectangular));

// 配置序列化设置
var settings = new JsonSerializerSettings
{
    Converters = new List<JsonConverter> { new TupleColorShapeConverter() },
    Formatting = Formatting.Indented // 可选,格式化JSON便于阅读
};

// 序列化
var json = JsonConvert.SerializeObject(shelvingUnit, settings);

// 反序列化
var deserializedUnit = JsonConvert.DeserializeObject<ShelvingUnit>(json, settings);

方式二:全局默认配置

如果业务代码中多处需要处理该类型,可以全局注册转换器,避免重复配置:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
    Converters = new List<JsonConverter> { new TupleColorShapeConverter() }
};

// 后续直接使用JsonConvert.SerializeObject/DeserializeObject即可
var json = JsonConvert.SerializeObject(shelvingUnit);
var deserializedUnit = JsonConvert.DeserializeObject<ShelvingUnit>(json);

3. 为什么之前的尝试未成功?

  • 修改JSON键格式:手动修改仅能临时解决单次反序列化问题,序列化时会重新生成默认格式,无法持久解决;
  • TypeConverter:Json.NET默认不会自动使用TypeConverter处理字典键的转换,需结合自定义转换器才能生效;
  • 拆分列表重建字典:复杂业务场景下易遗漏数据,且代码冗余,无法应对嵌套或动态数据结构。

内容的提问来源于stack exchange,提问作者Eric Eggers

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 01:40:19