将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个回答

746

使用 Json.NET 很简单:

dynamic stuff = JsonConvert.DeserializeObject("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");

string name = stuff.Name;
string address = stuff.Address.City;

还有使用 Newtonsoft.Json.Linq:

dynamic stuff = JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");

string name = stuff.Name;
string address = stuff.Address.City;

文档: 使用动态方式查询JSON


16
为了自省动态的 stuff,可以这样做:foreach (Newtonsoft.Json.Linq.JProperty jproperty in stuff) { Console.WriteLine("jproperty.Name = {0}", jproperty.Name);} - Matthias
12
JsonConvert.DeserializeObject和JObject.Parse之间的区别是什么?答案是它们在使用方式上完成相同的任务,但不解释它们之间的差异。 - cja
11
我已经尝试了这个方法,但它对我无效。它显示“JObject没有实现‘Name’”。 - Lee Louviere
5
@cja 没有区别:https://dev59.com/EGAg5IYBdhLWcg3wZ6PY - nawfal
13
我无法使它工作。我已经缩小了问题范围,发现问题在于 async 方法内部。如果我将该方法改为同步方法,则可以按预期正常工作。但是,如果将该方法更改为 async,我无法获得一个 dynamic,而只能获得一个 object。明确转换也没有用,仍然只会给我一个 object。还有其他人遇到这个问题吗? - codeConcussion
显示剩余7条评论

735

如果您愿意依赖于 System.Web.Helpers 程序集,那么您可以使用 Json 类:Json

dynamic data = Json.Decode(json);

它作为MVC框架的一个附加组件包含在.NET 4框架中。如果这有帮助,一定要给Vlad点个赞!但是,如果您不能假设客户端环境包括此DLL,请继续阅读。


这里提供了另一种反序列化方法 (点击跳转)。我稍微修改了代码以修复一个错误并适应我的编程风格。您只需要这段代码和对System.Web.Extensions的引用即可:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;

public sealed class DynamicJsonConverter : JavaScriptConverter
{
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");

        return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
    }

    #region Nested type: DynamicJsonObject

    private sealed class DynamicJsonObject : DynamicObject
    {
        private readonly IDictionary<string, object> _dictionary;

        public DynamicJsonObject(IDictionary<string, object> dictionary)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");
            _dictionary = dictionary;
        }

        public override string ToString()
        {
            var sb = new StringBuilder("{");
            ToString(sb);
            return sb.ToString();
        }

        private void ToString(StringBuilder sb)
        {
            var firstInDictionary = true;
            foreach (var pair in _dictionary)
            {
                if (!firstInDictionary)
                    sb.Append(",");
                firstInDictionary = false;
                var value = pair.Value;
                var name = pair.Key;
                if (value is string)
                {
                    sb.AppendFormat("{0}:\"{1}\"", name, value);
                }
                else if (value is IDictionary<string, object>)
                {
                    new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
                }
                else if (value is ArrayList)
                {
                    sb.Append(name + ":[");
                    var firstInArray = true;
                    foreach (var arrayValue in (ArrayList)value)
                    {
                        if (!firstInArray)
                            sb.Append(",");
                        firstInArray = false;
                        if (arrayValue is IDictionary<string, object>)
                            new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
                        else if (arrayValue is string)
                            sb.AppendFormat("\"{0}\"", arrayValue);
                        else
                            sb.AppendFormat("{0}", arrayValue);

                    }
                    sb.Append("]");
                }
                else
                {
                    sb.AppendFormat("{0}:{1}", name, value);
                }
            }
            sb.Append("}");
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            if (!_dictionary.TryGetValue(binder.Name, out result))
            {
                // return null to avoid exception.  caller can check for null this way...
                result = null;
                return true;
            }

            result = WrapResultObject(result);
            return true;
        }

        public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
        {
            if (indexes.Length == 1 && indexes[0] != null)
            {
                if (!_dictionary.TryGetValue(indexes[0].ToString(), out result))
                {
                    // return null to avoid exception.  caller can check for null this way...
                    result = null;
                    return true;
                }

                result = WrapResultObject(result);
                return true;
            }

            return base.TryGetIndex(binder, indexes, out result);
        }

        private static object WrapResultObject(object result)
        {
            var dictionary = result as IDictionary<string, object>;
            if (dictionary != null)
                return new DynamicJsonObject(dictionary);

            var arrayList = result as ArrayList;
            if (arrayList != null && arrayList.Count > 0)
            {
                return arrayList[0] is IDictionary<string, object> 
                    ? new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x))) 
                    : new List<object>(arrayList.Cast<object>());
            }

            return result;
        }
    }

    #endregion
}

你可以像这样使用它:

string json = ...;

var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });

dynamic obj = serializer.Deserialize(json, typeof(object));

因此,考虑到一个 JSON 字符串:

{
  "Items":[
    { "Name":"Apple", "Price":12.3 },
    { "Name":"Grape", "Price":3.21 }
  ],
  "Date":"21/11/2010"
}
以下代码将在运行时起作用:
dynamic data = serializer.Deserialize(json, typeof(object));

data.Date; // "21/11/2010"
data.Items.Count; // 2
data.Items[0].Name; // "Apple"
data.Items[0].Price; // 12.3 (as a decimal)
data.Items[1].Name; // "Grape"
data.Items[1].Price; // 3.21 (as a decimal)

3
我在使用 dynamic obj = serializer.Deserialize(json, typeof(object)); 代码时出现了错误,提示方法没有2个参数的重载。这是dll错了还是其他原因? - Stewie Griffin
3
我发现你的ToString方法对我不起作用,所以我重写了它。它可能有一些bug,但对于我的数据集来说它是有效的,因此我将在这里提供给任何其他遇到问题的人:http://pastebin.com/BiRmQZdz - Quantumplation
39
你可以使用 System.Web.Helpers.Json - 它提供了一个 Decode 方法,返回一个动态对象。我也已经把这个信息作为答案发布了。 - Vlad Iliescu
2
有时在js中,您会遇到带有特殊字符的字段,例如“background-color”。要在js中访问这些字段,您可以使用obj [“background-color”]。反序列化为动态对象后,如何从c#中访问此类字段?当然,我不能使用obj.background-color,而obj [“background-color”]似乎也无法正常工作。如果动态对象也可以像js一样同时作为字典访问,那就太好了。 - Radu Simionescu
2
@RaduSimionescu 我可能有点晚了,但也许这可以帮助未来的访问者。我遇到了同样的问题,只是字段名为 params(这是 C# 中的关键字)。除了 TryGetMember,您还可以重写 TryGetIndex,它会给您与 JS 中完全相同的行为。然后您就可以使用 obj["params"]obj["background-color"] 来处理尴尬的字段名。 - Martin Ender
显示剩余14条评论

311
你可以使用 System.Web.Helpers.Json 完成此操作 - 它的解码方法返回一个动态对象,您可以自行遍历该对象。
它包含在 System.Web.Helpers 程序集中(.NET 4.0)。
var dynamicObject = Json.Decode(jsonString);

29
FYI System.Web.Helpers.dll需要.NET 4.0,但未包含在.NET 4.0中。它可以通过安装ASP.NET MVC 3来进行安装。 - jbtule
7
您可以在 Visual Studio 2012 中的“程序集”下的“扩展”组中找到此程序集。 - W3Max
1
使用动态语言有什么问题吗?如果输入的 JSON 不包含属性,我们如何高效地处理异常? - Usama Khalil
5
如果你想给模型强类型,则确保使用Json.Decode<T>(string)方法。 - Mike
2
将此库添加到您的项目中:https://dev59.com/3Wsz5IYBdhLWcg3wOlOh - user565869

89

将简单的“字符串 JSON 数据”转换为对象,无需使用任何第三方 DLL 文件:

WebClient client = new WebClient();
string getString = client.DownloadString("https://graph.facebook.com/zuck");

JavaScriptSerializer serializer = new JavaScriptSerializer();
dynamic item = serializer.Deserialize<object>(getString);
string name = item["name"];

//note: JavaScriptSerializer in this namespaces
//System.Web.Script.Serialization.JavaScriptSerializer

注意:您也可以使用自定义对象。

Personel item = serializer.Deserialize<Personel>(getString);

5
我不明白。这是目前为止最简单的解决方案,但没有人提到它。 - cikatomo
2
是的,这很简单 :) 有时您需要序列化,但不想包含第三方dll。 - İbrahim Özbölük
你能详细说明一下如何通过 myObject["myprop"] 访问反序列化对象的动态性吗?我知道这是在运行时完成的,但是通过 myObject["myprop"] 访问它是有效的吗? - Royi Namir
1
你可以像这样反序列化你的对象:Personel item = serializer.Deserialize<Personel>(getString); 如果你使用动态对象,你也可以使用数组,一切都是可能的,就像每个对象一样。 - İbrahim Özbölük
3
要使用System.Web.Script.Serialization命名空间,您的项目需要引用System.Web.Extensions。 - StilgarISCA
显示剩余2条评论

87

.NET 4.0内置了一个库可以实现这个功能:

using System.Web.Script.Serialization;
JavaScriptSerializer jss = new JavaScriptSerializer();
var d = jss.Deserialize<dynamic>(str);

这是最简单的方法。


29
你尝试过这个吗?它返回Dictionary<string,object>。除非我漏掉了什么,你的例子并没有返回一个动态对象。 - sergiopereira
19
这个不管用,它只会返回一个以动态形式呈现的字典。 - mattmanser
56
@Peter Long,亲爱的朋友,我相信我没有清楚地陈述我的观点。让我尝试纠正我的错误。我知道什么是动态类型。但是这并不允许您传入一个JSON对象并使用d.code,您必须执行d["code"].Value,这不是大多数查找此答案的人想要的,我们已经知道如何获取词典,并将其转换为动态类型是完全浪费时间的。恕我直言,我不同意,先生。 - mattmanser
4
@mattmanser,“我们已经知道如何获取字典并将其转换为动态类型”。它不一定是一个字典。Json除了字典之外还有列表。而且列表和字典可以嵌套。我的代码可以处理所有这些情况。但是你的方法不能。 - Peter Long
4
@mattmanser说得没错;可以实现 IDynamicMetaObjectProvider(或使用例如ExpandoObject)来拦截属性并在内部字典中查找它们。 这与使用 dynamic 结合使用,使得可以使用 d.code 等代码。 把字典强制转换为动态类型有点毫无意义。 - Stephen Drew
显示剩余7条评论

46
你可以通过Newtonsoft.Json实现此目的。从NuGet安装它后,按照以下方式操作:
using Newtonsoft.Json;

dynamic results = JsonConvert.DeserializeObject<dynamic>(YOUR_JSON);

32

我来这里是为了找到一个关于.NET Core的答案,不使用任何第三方或额外的参考资料。如果你使用标准JsonSerializer类和ExpandoObject一起使用,它可以正常工作。以下是对我有效的示例:

using System.Text.Json;
using System.Dynamic;

dynamic json = JsonSerializer.Deserialize<ExpandoObject>(jsonText);
Console.WriteLine(json.name);

这段代码会打印出传入Deserialize方法的JSON文本中存在的name属性的字符串值。完美 - 没有额外的库,什么都没有。只是 .NET Core。

编辑:可能对于包含嵌套元素的多级JSON存在问题。对于单层平面对象有效。


它在 .net 6 中无法工作,有什么想法吗?我想读取具有元素数组的属性。 - Anirudha Gupta
它只适用于原始类型属性,因为expando对象通过名称处理属性读取并按原样返回值。问题在于Console.WriteLine通过调用ToString将值转换为字符串,对于原始类型将给出正确的值。对于数组,您可能会在输出中看到实际值而不是对象类型。 - Tengiz
请注意,使用 ExpandoObject 是昂贵的! - Mazdak Shojaie

29

JsonFx可以将JSON内容反序列化为动态对象。

将数据序列化/反序列化为动态类型(在.NET 4.0中默认):

var reader = new JsonReader(); var writer = new JsonWriter();

string input = @"{ ""foo"": true, ""array"": [ 42, false, ""Hello!"", null ] }";
dynamic output = reader.Read(input);
Console.WriteLine(output.array[0]); // 42
string json = writer.Write(output);
Console.WriteLine(json); // {"foo":true,"array":[42,false,"Hello!",null]}

26

还有一种方法是使用Newtonsoft.Json

dynamic stuff = Newtonsoft.Json.JsonConvert.DeserializeObject("{ color: 'red', value: 5 }");
string color = stuff.color;
int value = stuff.value;

20

我制作了一个使用Expando Objects的DynamicJsonConverter新版本。我使用expando对象,因为我想要使用Json.NET将动态内容序列化回JSON。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Web.Script.Serialization;

public static class DynamicJson
{
    public static dynamic Parse(string json)
    {
        JavaScriptSerializer jss = new JavaScriptSerializer();
        jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });

        dynamic glossaryEntry = jss.Deserialize(json, typeof(object)) as dynamic;
        return glossaryEntry;
    }

    class DynamicJsonConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");

            var result = ToExpando(dictionary);

            return type == typeof(object) ? result : null;
        }

        private static ExpandoObject ToExpando(IDictionary<string, object> dictionary)
        {
            var result = new ExpandoObject();
            var dic = result as IDictionary<String, object>;

            foreach (var item in dictionary)
            {
                var valueAsDic = item.Value as IDictionary<string, object>;
                if (valueAsDic != null)
                {
                    dic.Add(item.Key, ToExpando(valueAsDic));
                    continue;
                }
                var arrayList = item.Value as ArrayList;
                if (arrayList != null && arrayList.Count > 0)
                {
                    dic.Add(item.Key, ToExpando(arrayList));
                    continue;
                }

                dic.Add(item.Key, item.Value);
            }
            return result;
        }

        private static ArrayList ToExpando(ArrayList obj)
        {
            ArrayList result = new ArrayList();

            foreach (var item in obj)
            {
                var valueAsDic = item as IDictionary<string, object>;
                if (valueAsDic != null)
                {
                    result.Add(ToExpando(valueAsDic));
                    continue;
                }

                var arrayList = item as ArrayList;
                if (arrayList != null && arrayList.Count > 0)
                {
                    result.Add(ToExpando(arrayList));
                    continue;
                }

                result.Add(item);
            }
            return result;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
        }
    }
}

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