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

为Dictionary自定义DateRange类型的JsonConverter遇序列化异常问题

问题根源与解决方案

你的异常核心原因是JSON规范不允许对象作为字典的键,JSON对象的键必须是字符串类型。你当前在WriteAsPropertyName方法中直接输出了一个嵌套对象(WriteStartObject/WriteEndObject),这违反了JSON语法,直接导致序列化时出现"数据写入过多或过少"的错误。同时你的读取逻辑也完全不符合JSON的结构解析规则。

修正后的DateRangeConverter代码

public class DateRangeConverter : JsonConverter<DateRange>
{
    // 选一个不会出现在ISO日期格式里的分隔符
    private const string DateSeparator = "|";

    // 处理DateRange作为普通JSON值的读取(非字典键场景)
    public override DateRange? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.StartObject)
            throw new JsonException("DateRange必须以对象形式存在");
        
        DateTime? begin = null;
        DateTime? end = null;
        
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndObject)
                break;
            
            if (reader.TokenType == JsonTokenType.PropertyName)
            {
                var propName = reader.GetString();
                reader.Read();
                
                switch (propName)
                {
                    case nameof(DateRange.Begin):
                        begin = reader.GetDateTime();
                        break;
                    case nameof(DateRange.End):
                        end = reader.GetDateTime();
                        break;
                    default:
                        reader.Skip(); // 跳过未知属性
                        break;
                }
            }
        }
        
        if (begin == null || end == null)
            throw new JsonException("DateRange必须包含Begin和End字段");
        
        return new DateRange { Begin = begin.Value, End = end.Value };
    }

    // 处理DateRange作为字典键的读取逻辑
    public override DateRange ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.String)
            throw new JsonException("DateRange字典键必须是字符串格式");
        
        var keyStr = reader.GetString();
        if (string.IsNullOrEmpty(keyStr))
            throw new JsonException("DateRange字典键不能为空");
        
        var dateParts = keyStr.Split(new[] { DateSeparator }, StringSplitOptions.RemoveEmptyEntries);
        if (dateParts.Length != 2)
            throw new JsonException($"无效的DateRange键格式:{keyStr}");
        
        if (!DateTime.TryParse(dateParts[0], out var begin) || !DateTime.TryParse(dateParts[1], out var end))
            throw new JsonException($"无法从键解析DateRange:{keyStr}");
        
        return new DateRange { Begin = begin, End = end };
    }

    // 处理DateRange作为字典键的序列化逻辑
    public override void WriteAsPropertyName(Utf8JsonWriter writer, DateRange value, JsonSerializerOptions options)
    {
        // 将DateRange转为ISO日期拼接的字符串,符合JSON键的字符串要求
        var keyStr = $"{value.Begin:o}{DateSeparator}{value.End:o}";
        writer.WritePropertyName(keyStr);
    }

    // 处理DateRange作为普通JSON值的序列化逻辑
    public override void Write(Utf8JsonWriter writer, DateRange value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        writer.WriteString(nameof(DateRange.Begin), value.Begin);
        writer.WriteString(nameof(DateRange.End), value.End);
        writer.WriteEndObject();
    }
}

关键改动说明

  • WriteAsPropertyName:把DateRange转换为"BeginISO日期|EndISO日期"格式的字符串,严格遵循JSON键必须是字符串的规则。
  • ReadAsPropertyName:读取字符串键后,通过分隔符分割并解析出Begin和End两个日期值。
  • Read方法:重新实现了普通对象场景的读取逻辑,正确遍历JSON对象的属性并赋值。
  • 移除了原来错误的手动解析日期字段的逻辑,改用框架自带的日期解析方法,避免自定义解析的bug。

使用示例

在序列化/反序列化时注册转换器即可:

var jsonOptions = new JsonSerializerOptions();
jsonOptions.Converters.Add(new DateRangeConverter());

// 序列化字典
var dateRangeDict = new Dictionary<DateRange, List<MyObject>>();
// 添加数据...
var jsonStr = JsonSerializer.Serialize(dateRangeDict, jsonOptions);

// 反序列化
var deserializedDict = JsonSerializer.Deserialize<Dictionary<DateRange, List<MyObject>>>(jsonStr, jsonOptions);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 08:00:28