JSON与C# dynamic:为何这仍是痛点(以及如何解决)
\n\n你是否曾看着API返回的JSON,心想:“天哪,为什么你如此复杂?”尤其是当数据结构像圣彼得堡的天气一样变幻莫测时。经典例子是来自外部服务的响应,字段有时是字符串,有时是数组,有时甚至是null。或者当你需要快速解析JSON,但为其创建单独的类就像用显微镜来切割微芯片一样大材小用。
正是针对这种情况,C#提供了dynamic的魔法。Stack Overflow上“Deserialize JSON into C# dynamic object?”的问题获得了数千次浏览——这绝非偶然。在2025年,当微服务和动态API成为常态时,快速灵活地处理JSON的能力是开发者的超能力。
在本文中,我们将探讨在C#中将JSON反序列化为dynamic object的最佳实践。没有枯燥的理论,只有代码、示例和实用技巧。开始吧!
\n\n为什么用dynamic?而不是直接用JObject?
\n\n好问题。C#中有JObject(来自Newtonsoft.Json)和JsonDocument(来自System.Text.Json)。它们也能“即时”解析JSON。但dynamic提供了一个重要的东西——语法糖。
比较一下:
\n\n// 通过JObject\nvar json = JObject.Parse(rawJson);\nvar name = (string)json["user"]["name"];\n\n// 通过dynamic\ndynamic obj = JsonConvert.DeserializeObject<dynamic>(rawJson);\nvar name = obj.user.name;\n\n\n第二种方式读起来像普通代码,而不是“获取键、转换类型、检查null”。这在处理深层嵌套的JSON时尤其方便。
\n\n方法1:经典方案——Newtonsoft.Json (Json.NET)
\n\n是的,现在已经是2025年,但Newtonsoft.Json仍然活跃且健康。许多遗留项目,甚至一些现代项目,仍在使用它。为什么?灵活性、可扩展性和海量配置选项。
\n\n基本示例
\n\nusing Newtonsoft.Json;\n\nstring json = @"\n{\n 'name': '阿列克谢',\n 'age': 30,\n 'skills': ['C#', 'Python', 'SQL'],\n 'address': {\n 'city': '莫斯科',\n 'zip': 101000\n }\n}";\n\ndynamic obj = JsonConvert.DeserializeObject<dynamic>(json);\n\nConsole.WriteLine(obj.name); // 阿列克谢\nConsole.WriteLine(obj.age); // 30\nConsole.WriteLine(obj.skills[0]); // C#\nConsole.WriteLine(obj.address.city); // 莫斯科\n\n\n一切简单而优雅。但有一个问题:使用dynamic时,你会失去IntelliSense和编译时的类型检查。字段名称的错误只会在运行时暴露。因此:
- \n
- 仅在你无法控制的数据读取中使用
dynamic。 \n - 务必用try-catch包裹调用。 \n
- 不要滥用——如果JSON结构稳定,最好创建DTO类。 \n
方法2:现代方案——System.Text.Json
\n\n从.NET Core 3.0开始,微软推广System.Text.Json——快速、原生、无外部依赖。到2025年,这已成为新项目的标准。
但有一个陷阱:System.Text.Json默认不支持反序列化为dynamic。需要稍微变通一下。
方案A:通过JsonDocument和ExpandoObject
\n\nusing System.Text.Json;\nusing System.Dynamic;\n\nstring json = @"{""name"":""叶莲娜"",""age"":25,""active"":true}";\n\nusing JsonDocument doc = JsonDocument.Parse(json);\ndynamic obj = ConvertToExpando(doc.RootElement);\n\nConsole.WriteLine(obj.name); // 叶莲娜\nConsole.WriteLine(obj.age); // 25\nConsole.WriteLine(obj.active); // True\n\nstatic ExpandoObject ConvertToExpando(JsonElement element)\n{\n var expando = new ExpandoObject();\n var dict = (IDictionary<string, object>)expando;\n\n foreach (var property in element.Enumera