匿名类型无法直接转Dictionary,需用反射遍历属性;JSON反序列化前须确认输入为字符串;ExpandoObject可直转IDictionary;反射转换应兼容非string键并处理null及特殊类型。

Object转Dictionary失败时,先看是不是匿名类型
直接用 JsonConvert.DeserializeObject<dictionary object>></dictionary> 或强制转型 (Dictionary<string object>)obj</string> 报错,大概率是因为源 object 是 C# 匿名类型(比如 LINQ 查询返回的 new { A = 1, B = "x" })。它不是 Dictionary,也不是 IDictionary,连 GetType().GetProperties() 都得靠反射读,硬转必然失败。
- 检查方法:打印
obj.GetType().Name,如果带"f__AnonymousType"就是匿名类型 - 别试
as Dictionary<string object></string>—— 永远为null - 真要转字典,得走属性遍历:用
obj.GetType().GetProperties()取值,再塞进新Dictionary<string object></string> - 注意:只读属性(如 get-only auto-property)也能取,但带
[JsonIgnore]或[XmlIgnore]的不会自动跳过,得手动过滤
用JsonConvert反序列化前,确认对象是JSON字符串还是.NET对象
很多人卡在这步:把一个已经 deserialized 过的 object(比如 JObject 或 ExpandoObject)再喂给 JsonConvert.DeserializeObject,结果抛出 JsonReaderException: Unexpected character encountered —— 因为它收到的是 .NET 对象,不是 JSON 字符串。
- 如果是
string类型且内容像{"a":1,"b":"x"},才适合传给JsonConvert.DeserializeObject<dictionary>>()</dictionary> - 如果是
JObject,直接用jObj.ToObject<dictionary>>()</dictionary>,别先调jObj.ToString() - 如果是
ExpandoObject,它本身实现了IDictionary<string object></string>,可直接as IDictionary<string object></string>转 - 性能提醒:反复序列化/反序列化字符串开销大,尤其在循环里,优先走原生接口
反射转换Dictionary时,Key类型不一定是string
用反射把任意对象转成键值对集合时,很多人默认 Key 是 string,但实际业务中常有 int ID 做键、或枚举做键。硬写死 Dictionary<string object></string> 会丢类型信息或报错。
- 若源对象是
Dictionary<int string></int>,用as Dictionary<string object></string>一定失败 —— 类型不兼容 - 安全做法:先判断源类型是否实现
IDictionary,再用foreach (var kvp in (IDictionary)obj)遍历,Key 和 Value 保持原类型 - 需要统一为
string键?显式调用kvp.Key.ToString(),但注意nullKey 会崩,得提前判空 - 自定义类带索引器(
this[int i])不算IDictionary,反射也扫不到,别指望自动识别
Dictionary里嵌套复杂类型怎么保结构
把一个含 List、嵌套 class、DateTime 的对象转成 Dictionary<string object></string> 后,发现日期变成长数字、List 变成 object[]、子对象变 Dictionary<string object></string> —— 这不是 bug,是默认行为。JsonConvert 默认用 DateTimeZoneHandling.Roundtrip,但反射遍历不会管这个。
- 如果后续还要 JSON 序列化,建议全程用
JObject或JsonElement(.NET Core 3+),它们天然支持嵌套和类型保留 - 用反射手写转换时,对
DateTime、Guid、byte[]等特殊类型,别无脑.ToString(),按需转成 ISO 字符串或 Base64 -
Dictionary的 value 是null时,JsonConvert默认忽略该 key;反射转换则会保留null值 —— 行为不一致,得提前约定策略
事情说清了就结束。真正难的不是“怎么转”,而是转完之后谁来维护类型语义、空值含义、时间精度、嵌套边界——这些没写在代码里,但天天在 runtime 报错。


















