将JSON反序列化为C#动态对象?

1180

有没有一种方法可以将JSON内容反序列化为C#动态类型? 能够跳过创建大量的类以使用DataContractJsonSerializer将是很好的。


7
如果你需要一些“动态”的东西,为什么不使用大多数JSON解码器自带的get-style访问器,而不是转换成普通的对象?(例如,是否真的需要创建“动态”对象?)json.org上有许多C# JSON实现的链接。 - user166390
我正在开发一个项目,试图将外部依赖降至最低。因此,如果可以使用标准的 .net 序列化程序和类型来完成某些任务,那将是首选。当然,如果不可能,我会使用 json.org。谢谢! - jswanson
54
C#团队增加了“dynamic”关键字,这让我感到很惊讶。但是在CLR中却没有办法将JSON对象转换为动态的CLR类实例。 - Frank Schwieterman
2
很不幸,被采纳的答案在.NET 4 RTM中无法使用。我发布了一个帮助我开始使用它的答案,可能对其他人有用。 - Drew Noakes
更新于2018年10月,这就是魔法发生的方式:https://dev59.com/iGgu5IYBdhLWcg3winc3#48023576 - Ole EH Dufour
你应该注意到我遇到的问题:我非常幸运,在使用Visual Studio创建类并使用“编辑”>“特殊粘贴”>“将JSON粘贴为类”之后,才发现了这个伟大的线程。像那样创建类使得智能感知可以完美地适用于给定的JSON结构。这是绝对必要的,因为JSON字符串非常庞大-370k。它是使用“ytdlp --dump-json”获取有关YouTube视频的大量信息,其中包含数十个JSON项。 - Tonecops
32个回答

14

使用 Newtonsoft.Json 创建动态对象非常方便。

//json is your string containing the JSON value
dynamic data = JsonConvert.DeserializeObject<dynamic>(json);

现在您可以像访问普通对象一样访问data对象。这是我们目前示例中的JSON对象:

{ "ID":123,"Name":"Jack","Numbers":[1, 2, 3] }
这是反序列化后访问它的方法:
data.ID //Retrieve the int
data.Name //Retrieve the string
data.Numbers[0] //Retrieve the first element in the array

12

我使用http://json2csharp.com/来获取代表JSON对象的类。

输入:

{
   "name":"John",
   "age":31,
   "city":"New York",
   "Childs":[
      {
         "name":"Jim",
         "age":11
      },
      {
         "name":"Tim",
         "age":9
      }
   ]
}

输出:

public class Child
{
    public string name { get; set; }
    public int age { get; set; }
}

public class Person
{
    public string name { get; set; }
    public int age { get; set; }
    public string city { get; set; }
    public List<Child> Childs { get; set; }
}

然后我使用Newtonsoft.Json来填充类:

using Newtonsoft.Json;

namespace GitRepositoryCreator.Common
{
    class JObjects
    {
        public static string Get(object p_object)
        {
            return JsonConvert.SerializeObject(p_object);
        }
        internal static T Get<T>(string p_object)
        {
            return JsonConvert.DeserializeObject<T>(p_object);
        }
    }
}

您可以这样调用:
Person jsonClass = JObjects.Get<Person>(stringJson);

string stringJson = JObjects.Get(jsonClass);

备注:

如果您的JSON变量名不是有效的C#名称(名称以$开头),您可以像这样进行修复:

public class Exception
{
   [JsonProperty(PropertyName = "$id")]
   public string id { get; set; }
   public object innerException { get; set; }
   public string message { get; set; }
   public string typeName { get; set; }
   public string typeKey { get; set; }
   public int errorCode { get; set; }
   public int eventId { get; set; }
}

8

最简单的方法是:

只需要包含这个DLL文件

像这样使用代码:

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

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.

8

这个答案节省了我很多时间!应该选择为最佳答案! - jsiot

6

试试这个:

  var units = new { Name = "Phone", Color= "White" };
    var jsonResponse = JsonConvert.DeserializeAnonymousType(json, units);

到目前为止,我最喜欢的方法 - znn
老铁,给你一个拥抱 +1 :D - Shahroozevsky

6
您可以使用 using Newtonsoft.Json
var jRoot = 
 JsonConvert.DeserializeObject<dynamic>(Encoding.UTF8.GetString(resolvedEvent.Event.Data));
resolvedEvent.Event.Data是从调用核心事件获取的响应。

6
您可以扩展JavaScriptSerializer,将其递归地复制到expando对象中创建的字典中,然后动态使用它们。
static class JavaScriptSerializerExtensions
{
    public static dynamic DeserializeDynamic(this JavaScriptSerializer serializer, string value)
    {
        var dictionary = serializer.Deserialize<IDictionary<string, object>>(value);
        return GetExpando(dictionary);
    }

    private static ExpandoObject GetExpando(IDictionary<string, object> dictionary)
    {
        var expando = (IDictionary<string, object>)new ExpandoObject();

        foreach (var item in dictionary)
        {
            var innerDictionary = item.Value as IDictionary<string, object>;
            if (innerDictionary != null)
            {
                expando.Add(item.Key, GetExpando(innerDictionary));
            }
            else
            {
                expando.Add(item.Key, item.Value);
            }
        }

        return (ExpandoObject)expando;
    }
}

然后,您只需要在您定义扩展名的命名空间中使用using语句(考虑仅在System.Web.Script.Serialization中定义它们...另一个技巧是不使用命名空间,那么您根本不需要使用using语句),就可以像这样使用它们:

var serializer = new JavaScriptSerializer();
var value = serializer.DeserializeDynamic("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");

var name = (string)value.Name; // Jon Smith
var age = (int)value.Age;      // 42

var address = value.Address;
var city = (string)address.City;   // New York
var state = (string)address.State; // NY

5
请看我在CodeProject写的一篇文章,它准确回答了这个问题:

使用JSON.NET创建动态类型

由于那篇文章还有一个包含关键/必需源文件的附件,所以不必在此重复所有内容。

5

我在我的代码中使用了这样的方法,它运行良好

using System.Web.Script.Serialization;
JavaScriptSerializer oJS = new JavaScriptSerializer();
RootObject oRootObject = new RootObject();
oRootObject = oJS.Deserialize<RootObject>(Your JSon String);

1
但这不是问题所问的。当您必须为每个JSON字符串指定类型并使用动态类型时,会有所不同。 - Illuminati

5
使用JSON.NET进行反序列化可以使用该库中包含的JObject类进行动态操作。我的JSON字符串表示这些类:
public class Foo {
   public int Age {get;set;}
   public Bar Bar {get;set;}
}

public class Bar {
   public DateTime BDay {get;set;}
}

现在我们不引用上述类对字符串进行反序列化:
var dyn = JsonConvert.DeserializeObject<JObject>(jsonAsFooString);

JProperty propAge = dyn.Properties().FirstOrDefault(i=>i.Name == "Age");
if(propAge != null) {
    int age = int.Parse(propAge.Value.ToString());
    Console.WriteLine("age=" + age);
}

//or as a one-liner:
int myage = int.Parse(dyn.Properties().First(i=>i.Name == "Age").Value.ToString());

或者,如果你想更深入了解:

var propBar = dyn.Properties().FirstOrDefault(i=>i.Name == "Bar");
if(propBar != null) {
    JObject o = (JObject)propBar.First();
    var propBDay = o.Properties().FirstOrDefault (i => i.Name=="BDay");
    if(propBDay != null) {
        DateTime bday = DateTime.Parse(propBDay.Value.ToString());
        Console.WriteLine("birthday=" + bday.ToString("MM/dd/yyyy"));
    }
}

//or as a one-liner:
DateTime mybday = DateTime.Parse(((JObject)dyn.Properties().First(i=>i.Name == "Bar").First()).Properties().First(i=>i.Name == "BDay").Value.ToString());

完整示例请参见此文章


这种方法允许“遍历”jSON文档,以便您可以处理JSON结构未知或可变的情况(例如,当发生错误时,许多API返回完全不同的JSON文档)。除了Newtonsoft.JSON(又名JSON.NET)之外,还有其他库可以实现这一点吗? - Alex 75

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