Dictionary序列化键名丢失或变小写,源于Newtonsoft.Json默认驼峰规则或System.Text.Json启用CamelCase;反序列化数组到Dictionary报错因类型不匹配;value为复杂类型时需确保构造函数和访问性;性能问题多因滥用JToken/JsonElement未及时释放。

Dictionary 序列化后键名丢失或变成小写
默认用 System.Text.Json 序列化 Dictionary<string object></string> 时,键会原样保留;但如果你用的是 Newtonsoft.Json(即 Json.NET),且没关驼峰命名规则,"UserName" 这种键会被转成 "userName" —— 不是数据丢了,是被自动改写了。
实操建议:
- 确认你用的是哪个库:
System.Text.Json还是Newtonsoft.Json,两者的默认行为完全不同 - 如果是
Newtonsoft.Json,检查是否启用了ContractResolver = new CamelCasePropertyNamesContractResolver(),它会影响字典键名 - 想强制保持原始键名?加这个配置:
new JsonSerializerSettings { ContractResolver = new DefaultContractResolver() } -
System.Text.Json默认不碰字典键,但若用了JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase,它也会改键名 —— 字典键不是“属性”,但新版(.NET 6+)确实会受此影响
反序列化 JSON 到 Dictionary 时报错 “Cannot deserialize the current JSON array”
常见于把一个 JSON 数组(比如 [{"id":1},{"id":2}])直接往 Dictionary<string string></string> 里塞。JSON 数组 ≠ JSON 对象,而 Dictionary 只能对应对象(即 {} 开头的结构)。
实操建议:
- 先用文本工具或在线 JSON 校验器看一眼原始数据:是
[...]还是{...}?别靠猜测 - 如果是数组,想转成字典集合,目标类型该是
List<dictionary object>></dictionary>,不是单个Dictionary - 如果服务端返回格式不固定(有时对象、有时数组),别硬解,先用
JsonElement或JToken做类型探测再分支处理 - 用
System.Text.Json时,JsonSerializer.Deserialize<dictionary object>>(json)</dictionary>遇到数组会直接抛JsonException,错误信息里明确写了 “Cannot deserialize the current JSON array” —— 这就是线索
Dictionary 的 value 是复杂类型,反序列化后为 null
典型场景:JSON 里 "data": {"name":"Alice"},你定义了 Dictionary<string person></string>,结果 dict["data"] 是 null。这不是 bug,是类型擦除导致的反序列化失败。
实操建议:
-
Dictionary<string object></string>是安全兜底方案,所有值都可进,但后续要手动转型 - 如果坚持用
Dictionary<string person></string>,必须确保Person类型有无参构造函数,且所有字段/属性可被 public 访问(System.Text.Json默认不支持 private set / init-only 属性,除非显式配JsonSerializerOptions.IncludeFields = true) - Newtonsoft.Json 更宽容些,但若
Person有[JsonIgnore]或自定义 converter,也得同步检查 - 别忽略 nullability:如果 JSON 里
"data"字段缺失或为null,而你的Person是非 nullable 引用类型(C# 8+),反序列化可能静默失败或报错
性能差、内存暴涨,只因用了 Dictionary
有人图省事,把整个 JSON 解成 Dictionary<string jtoken></string>(Newtonsoft)或 Dictionary<string jsonelement></string>(STJ),结果发现 GC 压力大、响应变慢。问题不在字典本身,而在底层 token 持有整个 JSON 文档的引用。
实操建议:
-
JToken是“活”的解析树,每次访问子节点都可能触发懒解析,且生命周期难控制;JsonElement更轻,但必须在JsonDocument有效期内使用,超出就抛InvalidOperationException: Cannot access child value on a value of type Undefined - 真需要灵活读取?优先用
JsonNode(.NET 6+),它是可变、可序列化的轻量结构,比JToken内存友好 - 如果只是读几个字段,别全量加载:用
JsonDocument.Parse(json).RootElement.GetProperty("user").GetProperty("id").GetInt32(),避免创建中间字典 - 特别注意:
JsonDocument必须Dispose(),否则内存泄漏;JObject.Parse()同理,别让它活在 long-lived 对象里
最常被跳过的一步:没验证 JSON 字符串本身是否合法,就直接扔给反序列化器。空字符串、BOM 头、HTML 实体编码(如 ")、意外的换行符,都会让解析器在第一行就跪 —— 先 Trim(),再 IsValidJson()(自己写个简版校验),比堆 try-catch 实在得多。


















