使用Json.net将JSON对象反序列化为动态对象

501

使用Json.net反序列化后,是否可以返回一个动态对象?我想做这样的事情:

dynamic jsonResponse = JsonConvert.Deserialize(json);
Console.WriteLine(jsonResponse.message);

3
考虑从JSON生成C#类http://json2csharp.com/,并使用生成的类而不是动态类。 - Michael Freidgeim
2
可能是将JSON反序列化为C#动态对象?的重复问题。 - meJustAndrew
你认为StackOverflow应该如何将一个问题标记为“过时”? 已经过去六年了,虽然有针对每个版本的.NET的有效答案和合理建议,但是它们已经太多了,不再有帮助。 - andrew lorien
这个有帮助吗?https://dev59.com/RFkR5IYBdhLWcg3w2wt1#70026040 - Ash K
这个有帮助吗?https://stackoverflow.com/a/70026040/8644294 - undefined
8个回答

619

Json.NET允许我们做到这一点:

dynamic d = JObject.Parse("{number:1000, str:'string', array: [1,2,3,4,5,6]}");

Console.WriteLine(d.number);
Console.WriteLine(d.str);
Console.WriteLine(d.array.Count);

输出:

 1000
 string
 6

查看文档: 使用Json.NET进行LINQ to JSON

另请参阅JObject.ParseJArray.Parse


43
请注意,对于数组,语法为JArray.Parse - jgillich
7
为什么我们需要使用动态字?我很害怕从未使用过:D - Furkan Gözükara
4
在 VB.Net 中,您需要执行 Dim d As Object = JObject.Parse("{number:1000, str:'string', array: [1,2,3,4,5,6]}") - ilans
3
@MonsterMMORPG 你应该是 :) 动态(Dynamic)在几乎所有情况下都是一种反模式,但是有时候你可能会遇到一个合理使用它的情况。 - Pluc
4
在.NET 4.5.2中使用Newtonsoft.Json 8.0.3时,可能会发生Microsoft.CSharp.RuntimeBinder.RuntimeBinderException异常。错误信息为"'Newtonsoft.Json.Linq.JObject'不包含名为'number'的定义"。 - user4698855
显示剩余5条评论

119

从Json.NET 4.0 Release 1开始,它支持原生的动态支持:

[Test]
public void DynamicDeserialization()
{
    dynamic jsonResponse = JsonConvert.DeserializeObject("{\"message\":\"Hi\"}");
    jsonResponse.Works = true;
    Console.WriteLine(jsonResponse.message); // Hi
    Console.WriteLine(jsonResponse.Works); // True
    Console.WriteLine(JsonConvert.SerializeObject(jsonResponse)); // {"message":"Hi","Works":true}
    Assert.That(jsonResponse, Is.InstanceOf<dynamic>());
    Assert.That(jsonResponse, Is.TypeOf<JObject>());
}

当然,获取当前版本的最佳方式是通过 NuGet。

更新(11/12/2014)以回应评论:

这个方法非常完美。如果你在调试器中检查类型,你会看到这个值实际上是动态的。它的底层类型JObject。如果你想控制类型(比如指定ExpandoObject),那就这样做。

输入图像描述


24
似乎这永远都不起作用,它只返回一个 JObject,而不是一个动态变量。 - Paul
16
顺便提一下,这个方法可行:JsonConvert.DeserializeObject<ExpandoObject>(STRING); 经过适当的反序列化处理,所以我们不需要使用JObject等。 - Gutek
2
@Gutek不确定您遇到了什么问题。您运行了代码吗?我向测试中添加了断言,并添加了一个原始JSON中不存在的属性。调试器的截图已包含在内。 - David Peden
1
@DavidPeden 如果您有 JObject 并尝试在 Razor 中绑定它,您将会得到异常。问题是关于反序列化为动态对象 - JObject 是动态的,但包含“自己”的类型,如 JValue 而不是原始类型。如果返回类型是 JValue,则无法在 Razor 中使用 @Model.Prop 名称。 - Gutek
2
这个可以运行,但每个动态属性都是一个 JValue。这让我感到困惑,因为我在调试器/立即窗口中工作时并没有看到只有 string。David 在底部的截图中展示了这一点。JValue 是可转换的,所以你可以直接使用 string m = jsonResponse.message - Luke Puplett
显示剩余3条评论

83

如果你只是将其反序列化为 dynamic,你会得到一个 JObject。通过使用 ExpandoObject,你可以得到你想要的内容。

var converter = new ExpandoObjectConverter();    
dynamic message = JsonConvert.DeserializeObject<ExpandoObject>(jsonString, converter);

2
结果也可以转换为字典。 - FindOutIslamNow
1
正是我所寻找的!谢谢! - DarkDeny

49

我知道这是旧帖子了,但 JsonConvert 实际上有一个不同的方法,因此它应该是

var product = new { Name = "", Price = 0 };
var jsonResponse = JsonConvert.DeserializeAnonymousType(json, product);

28
这将把 JSON 数据反序列化为匿名类型,而不是动态类型。匿名类型和动态类型是两个不同的概念,我认为这并没有回答所问的问题。 - jrista
1
需要使用两个变量吗?为什么不在第二个语句中重复使用第一个变量呢? - RenniePet

39

你可以使用JsonConvert.DeserializeObject来实现。只需简单执行以下操作:

dynamic jsonResponse = JsonConvert.DeserializeObject(json);
Console.WriteLine(jsonResponse["message"]);

2
JsonConvert 不包含名为 Deserialize 的方法。 - Can Poyrazoğlu
1
应该只是DeserializeObject,但在我看来这应该是被接受的答案。 - superjugy
1
这个可行 :D 非常感谢,其他所有答案对我都没用。我一直在尝试使用DeserializeObject,但是我使用的语法是针对普通类对象的,而不是jsonResponse["message"]。 - Sasino

23

注意:我在2010年回答这个问题时,没有办法在没有类型的情况下进行反序列化,这种方法允许您在不定义实际类的情况下进行反序列化,并允许使用匿名类来执行反序列化。


你需要一些类型来进行反序列化。您可以按照以下方式进行操作:

var product = new { Name = "", Price = 0 };
dynamic jsonResponse = JsonConvert.Deserialize(json, product.GetType());

我的答案基于.NET 4.0内置的JSON序列化器的解决方案。在此处链接到反序列化为匿名类型:

http://blogs.msdn.com/b/alexghi/archive/2008/12/22/using-anonymous-types-to-deserialize-json-data.aspx


我支持你,Phill。不知道为什么有人会给你点踩,如果有人能够……请解释一下为什么? - PEO
20
他们正在踩这个问题,因为它是关于无类型反序列化的。 - richard
4
在2010年撰写此答案时,它是有效的,因为当时没有其他解决方案。甚至在支持JSON.NET之前的一段时间内,它也被接受为正确答案。 - Phill
1
这不会产生一个动态对象。它会产生一个JObject,你可以将其引用为动态对象。但它仍然是一个JObject。 - ghostbust555
1
@ghostbust555,所以和 dynamic d = JObject.Parse... 没有区别。 - Phill

7
是的,这是可能的。我一直在这样做。
dynamic Obj = JsonConvert.DeserializeObject(<your json string>);

对于非母语类型来说,这有点棘手。假设在您的Obj中,有一个ClassA和ClassB对象。它们都被转换为JObject。您需要做的是:

ClassA ObjA = Obj.ObjA.ToObject<ClassA>();
ClassB ObjB = Obj.ObjB.ToObject<ClassB>();

7
如果您使用旧版本的JSON.NET,没有JObject对象,那么这是另一种从JSON创建动态对象的简单方法: https://github.com/chsword/jdynamic NuGet安装
PM> Install-Package JDynamic

支持使用字符串索引访问成员,例如:

dynamic json = new JDynamic("{a:{a:1}}");
Assert.AreEqual(1, json["a"]["a"]);

测试用例

您可以按照以下方式使用此实用程序:

直接获取值

dynamic json = new JDynamic("1");

//json.Value

2.获取json对象中的成员

dynamic json = new JDynamic("{a:'abc'}");
//json.a is a string "abc"

dynamic json = new JDynamic("{a:3.1416}");
//json.a is 3.1416m

dynamic json = new JDynamic("{a:1}");
//json.a is integer: 1

3.IEnumerable

dynamic json = new JDynamic("[1,2,3]");
/json.Length/json.Count is 3
//And you can use json[0]/ json[2] to get the elements

dynamic json = new JDynamic("{a:[1,2,3]}");
//json.a.Length /json.a.Count is 3.
//And you can use  json.a[0]/ json.a[2] to get the elements

dynamic json = new JDynamic("[{b:1},{c:1}]");
//json.Length/json.Count is 2.
//And you can use the  json[0].b/json[1].c to get the num.

其他

dynamic json = new JDynamic("{a:{a:1} }");

//json.a.a is 1.

这非常好。我已经寻找类似的东西一段时间了……完全符合要求。 - jim tollan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接