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

5

我想在单元测试中以程序化方式完成这项任务,我有幸不需要手动输入。

我的解决方案是:

var dict = JsonConvert.DeserializeObject<ExpandoObject>(json) as IDictionary<string, object>;

现在我可以断言

dict.ContainsKey("ExpectedProperty");

4

有一个轻量级的C# JSON库,叫做SimpleJson

它支持.NET 3.5+、Silverlight和Windows Phone 7。

它支持.NET 4.0的动态功能。

也可以作为NuGet包安装。

Install-Package SimpleJson

是的,但你怎么使用它?回答不够好。 - Fandango68
希望这可以帮到你: https://csharp.hotexamples.com/examples/-/SimpleJson/-/php-simplejson-class-examples.html - Techiemanu

4

如何使用动态和JavaScriptSerializer解析简单的JSON内容

请添加 System.Web.Extensions 的引用,并在顶部添加此命名空间 using System.Web.Script.Serialization;

public static void EasyJson()
{
    var jsonText = @"{
        ""some_number"": 108.541,
        ""date_time"": ""2011-04-13T15:34:09Z"",
        ""serial_number"": ""SN1234""
    }";

    var jss = new JavaScriptSerializer();
    var dict = jss.Deserialize<dynamic>(jsonText);

    Console.WriteLine(dict["some_number"]);
    Console.ReadLine();
}

如何使用动态和JavaScriptSerializer解析嵌套和复杂的json

请在顶部添加System.Web.Extensions的引用,并添加此命名空间using System.Web.Script.Serialization;

public static void ComplexJson()
{
    var jsonText = @"{
        ""some_number"": 108.541,
        ""date_time"": ""2011-04-13T15:34:09Z"",
        ""serial_number"": ""SN1234"",
        ""more_data"": {
            ""field1"": 1.0,
            ""field2"": ""hello""
        }
    }";

    var jss = new JavaScriptSerializer();
    var dict = jss.Deserialize<dynamic>(jsonText);

    Console.WriteLine(dict["some_number"]);
    Console.WriteLine(dict["more_data"]["field2"]);
    Console.ReadLine();
}

4

获取一个 ExpandoObject:

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

Container container = JsonConvert.Deserialize<Container>(jsonAsString, new ExpandoObjectConverter());

4
使用C#中的DataSet和JavaScript。使用一个简单的函数来创建一个带有DataSet输入的JSON流。像这样创建JSON内容(多表数据集):
[[{a:1,b:2,c:3},{a:3,b:5,c:6}],[{a:23,b:45,c:35},{a:58,b:59,c:45}]]

只需在客户端使用eval函数。例如:
var d = eval('[[{a:1,b:2,c:3},{a:3,b:5,c:6}],[{a:23,b:45,c:35},{a:58,b:59,c:45}]]')

然后使用:

d[0][0].a // out 1 from table 0 row 0

d[1][1].b // out 59 from table 1 row 1

// Created by Behnam Mohammadi And Saeed Ahmadian
public string jsonMini(DataSet ds)
{
    int t = 0, r = 0, c = 0;
    string stream = "[";

    for (t = 0; t < ds.Tables.Count; t++)
    {
        stream += "[";
        for (r = 0; r < ds.Tables[t].Rows.Count; r++)
        {
            stream += "{";
            for (c = 0; c < ds.Tables[t].Columns.Count; c++)
            {
                stream += ds.Tables[t].Columns[c].ToString() + ":'" +
                          ds.Tables[t].Rows[r][c].ToString() + "',";
            }
            if (c>0)
                stream = stream.Substring(0, stream.Length - 1);
            stream += "},";
        }
        if (r>0)
            stream = stream.Substring(0, stream.Length - 1);
        stream += "],";
    }
    if (t>0)
        stream = stream.Substring(0, stream.Length - 1);
    stream += "];";
    return stream;
}

4
您想要的DynamicJSONObject对象包含在ASP.NET Web Pages包中的System.Web.Helpers.dll中,该包是WebMatrix的一部分。

4

为此,我将使用JSON.NET对JSON流进行低级解析,然后通过ExpandoObject类的实例构建对象层次结构。


1
一个例子将有助于更广泛的受众。 - singhswat
我不明白为什么这个回答比得到700多赞和被标记为正确答案的那个回答还要靠前!? - Vidar
@Vidar 你是按照最早的排序而非投票数来进行排序吗? - Daniel Earwicker

3
使用Cinchoo ETL - 一款可用于将JSON解析为动态对象的开源库:
string json = @"{
    ""key1"": [
        {
            ""action"": ""open"",
            ""timestamp"": ""2018-09-05 20:46:00"",
            ""url"": null,
            ""ip"": ""66.102.6.98""
        }
    ]
}";
using (var p = ChoJSONReader.LoadText(json)
    .WithJSONPath("$..key1")
    )
{
    foreach (var rec in p)
    {
        Console.WriteLine("Action: " + rec.action);
        Console.WriteLine("Timestamp: " + rec.timestamp);
        Console.WriteLine("URL: " + rec.url);
        Console.WriteLine("IP address: " + rec.ip);
    }
}

输出:

Action: open
Timestamp: 2018-09-05 20:46:00
URL: http://www.google.com
IP address: 66.102.6.98

示例代码:https://dotnetfiddle.net/S0ehSV

欲了解更多信息,请访问codeproject文章

免责声明:本库作者为本人。


2
我真的很喜欢System.Web.Helpers。
dynamic data = Json.Decode(json);

因为它支持像这样的用法

var val = data.Members.NumberTen;

或者

var val data.Members["10"];

关于System.Web.Helpers.DLL的引用真的很疯狂,甚至不适用于控制台和桌面应用程序。这是我尝试从https://github.com/mono/aspnetwebstack/tree/master/src/System.Web.Helpers中直接提取相同功能的独立文件的方式。(仅供教育目的分享)

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using Microsoft.CSharp.RuntimeBinder;
using System.Web.Script.Serialization;
using System.IO;
using System.Collections;
using System.Linq;
using System.Globalization;

namespace System.Web.Helpers
{
    public static class Json
    {
        private static readonly JavaScriptSerializer _serializer = CreateSerializer();

        public static string Encode(object value)
        {
            // Serialize our dynamic array type as an array
            DynamicJsonArray jsonArray = value as DynamicJsonArray;
            if (jsonArray != null)
            {
                return _serializer.Serialize((object[])jsonArray);
            }

            return _serializer.Serialize(value);
        }

        public static void Write(object value, TextWriter writer)
        {
            writer.Write(_serializer.Serialize(value));
        }

        public static dynamic Decode(string value)
        {
            return WrapObject(_serializer.DeserializeObject(value));
        }

        public static dynamic Decode(string value, Type targetType)
        {
            return WrapObject(_serializer.Deserialize(value, targetType));
        }

        public static T Decode<T>(string value)
        {
            return _serializer.Deserialize<T>(value);
        }

        private static JavaScriptSerializer CreateSerializer()
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            serializer.RegisterConverters(new[] { new DynamicJavaScriptConverter() });
            return serializer;
        }
        internal class DynamicJavaScriptConverter : JavaScriptConverter
        {
            public override IEnumerable<Type> SupportedTypes
            {
                get
                {
                    yield return typeof(IDynamicMetaObjectProvider);
                    yield return typeof(DynamicObject);
                }
            }

            public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
            {
                throw new NotSupportedException();
            }

            public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
            {
                Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
                IEnumerable<string> memberNames = DynamicHelper.GetMemberNames(obj);
                foreach (string item in memberNames)
                {
                    dictionary[item] = DynamicHelper.GetMemberValue(obj, item);
                }

                return dictionary;
            }
        }
        internal static dynamic WrapObject(object value)
        {
            // The JavaScriptSerializer returns IDictionary<string, object> for objects
            // and object[] for arrays, so we wrap those in different dynamic objects
            // so we can access the object graph using dynamic
            var dictionaryValues = value as IDictionary<string, object>;
            if (dictionaryValues != null)
            {
                return new DynamicJsonObject(dictionaryValues);
            }

            var arrayValues = value as object[];
            if (arrayValues != null)
            {
                return new DynamicJsonArray(arrayValues);
            }

            return value;
        }

    }
    // REVIEW: Consider implementing ICustomTypeDescriptor and IDictionary<string, object>
    public class DynamicJsonObject : DynamicObject
    {
        private readonly IDictionary<string, object> _values;

        public DynamicJsonObject(IDictionary<string, object> values)
        {
            Debug.Assert(values != null);
            _values = values.ToDictionary(p => p.Key, p => Json.WrapObject(p.Value),
                                          StringComparer.OrdinalIgnoreCase);
        }

        public override bool TryConvert(ConvertBinder binder, out object result)
        {
            result = null;
            if (binder.Type.IsAssignableFrom(_values.GetType()))
            {
                result = _values;
            }
            else
            {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "HelpersResources.Json_UnableToConvertType", binder.Type));
            }
            return true;
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = GetValue(binder.Name);
            return true;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            _values[binder.Name] = Json.WrapObject(value);
            return true;
        }

        public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
        {
            string key = GetKey(indexes);
            if (!String.IsNullOrEmpty(key))
            {
                _values[key] = Json.WrapObject(value);
            }
            return true;
        }

        public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
        {
            string key = GetKey(indexes);
            result = null;
            if (!String.IsNullOrEmpty(key))
            {
                result = GetValue(key);
            }
            return true;
        }

        private static string GetKey(object[] indexes)
        {
            if (indexes.Length == 1)
            {
                return (string)indexes[0];
            }
            // REVIEW: Should this throw?
            return null;
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            return _values.Keys;
        }

        private object GetValue(string name)
        {
            object result;
            if (_values.TryGetValue(name, out result))
            {
                return result;
            }
            return null;
        }
    }
    [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "This class isn't meant to be used directly")]
    public class DynamicJsonArray : DynamicObject, IEnumerable<object>
    {
        private readonly object[] _arrayValues;

        public DynamicJsonArray(object[] arrayValues)
        {
            Debug.Assert(arrayValues != null);
            _arrayValues = arrayValues.Select(Json.WrapObject).ToArray();
        }

        public int Length
        {
            get { return _arrayValues.Length; }
        }

        public dynamic this[int index]
        {
            get { return _arrayValues[index]; }
            set { _arrayValues[index] = Json.WrapObject(value); }
        }

        public override bool TryConvert(ConvertBinder binder, out object result)
        {
            if (_arrayValues.GetType().IsAssignableFrom(binder.Type))
            {
                result = _arrayValues;
                return true;
            }
            return base.TryConvert(binder, out result);
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            // Testing for members should never throw. This is important when dealing with
            // services that return different json results. Testing for a member shouldn't throw,
            // it should just return null (or undefined)
            result = null;
            return true;
        }

        public IEnumerator GetEnumerator()
        {
            return _arrayValues.GetEnumerator();
        }

        private IEnumerable<object> GetEnumerable()
        {
            return _arrayValues.AsEnumerable();
        }

        IEnumerator<object> IEnumerable<object>.GetEnumerator()
        {
            return GetEnumerable().GetEnumerator();
        }

        [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "This class isn't meant to be used directly")]
        public static implicit operator object[](DynamicJsonArray obj)
        {
            return obj._arrayValues;
        }

        [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "This class isn't meant to be used directly")]
        public static implicit operator Array(DynamicJsonArray obj)
        {
            return obj._arrayValues;
        }
    }

    /// <summary>
    /// Helper to evaluate different method on dynamic objects
    /// </summary>
    public static class DynamicHelper
    {
        // We must pass in "object" instead of "dynamic" for the target dynamic object because if we use dynamic, the compiler will
        // convert the call to this helper into a dynamic expression, even though we don't need it to be.  Since this class is internal,
        // it cannot be accessed from a dynamic expression and thus we get errors.

        // Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
        public static bool TryGetMemberValue(object obj, string memberName, out object result)
        {
            try
            {
                result = GetMemberValue(obj, memberName);
                return true;
            }
            catch (RuntimeBinderException)
            {
            }
            catch (RuntimeBinderInternalCompilerException)
            {
            }

            // We catch the C# specific runtime binder exceptions since we're using the C# binder in this case
            result = null;
            return false;
        }

        // Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to swallow exceptions that happen during runtime binding")]
        public static bool TryGetMemberValue(object obj, GetMemberBinder binder, out object result)
        {
            try
            {
                // VB us an instance of GetBinderAdapter that does not implement FallbackGetMemeber. This causes lookup of property expressions on dynamic objects to fail.
                // Since all types are private to the assembly, we assume that as long as they belong to CSharp runtime, it is the right one. 
                if (typeof(Binder).Assembly.Equals(binder.GetType().Assembly))
                {
                    // Only use the binder if its a C# binder.
                    result = GetMemberValue(obj, binder);
                }
                else
                {
                    result = GetMemberValue(obj, binder.Name);
                }
                return true;
            }
            catch
            {
                result = null;
                return false;
            }
        }

        // Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
        public static object GetMemberValue(object obj, string memberName)
        {
            var callSite = GetMemberAccessCallSite(memberName);
            return callSite.Target(callSite, obj);
        }

        // Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
        public static object GetMemberValue(object obj, GetMemberBinder binder)
        {
            var callSite = GetMemberAccessCallSite(binder);
            return callSite.Target(callSite, obj);
        }

        // dynamic d = new object();
        // object s = d.Name;
        // The following code gets generated for this expression:
        // callSite = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "Name", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
        // callSite.Target(callSite, d);
        // typeof(Program) is the containing type of the dynamic operation.
        // Dev10 Bug 914027 - Changed the callsite's target parameter from dynamic to object, see comment at top for details
        public static CallSite<Func<CallSite, object, object>> GetMemberAccessCallSite(string memberName)
        {
            var binder = Binder.GetMember(CSharpBinderFlags.None, memberName, typeof(DynamicHelper), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
            return GetMemberAccessCallSite(binder);
        }

        // Dev10 Bug 914027 - Changed the callsite's target parameter from dynamic to object, see comment at top for details
        public static CallSite<Func<CallSite, object, object>> GetMemberAccessCallSite(CallSiteBinder binder)
        {
            return CallSite<Func<CallSite, object, object>>.Create(binder);
        }

        // Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details
        public static IEnumerable<string> GetMemberNames(object obj)
        {
            var provider = obj as IDynamicMetaObjectProvider;
            Debug.Assert(provider != null, "obj doesn't implement IDynamicMetaObjectProvider");

            Expression parameter = Expression.Parameter(typeof(object));
            return provider.GetMetaObject(parameter).GetDynamicMemberNames();
        }
    }

}

2
"最初的回答",尝试这种方法!JSON示例:
[{
    "id": 140,
    "group": 1,
    "text": "xxx",
    "creation_date": 123456,
    "created_by": "xxx@gmail.co",
    "tags": ["xxxxx"]
  }, {
    "id": 141,
    "group": 1,
    "text": "xxxx",
    "creation_date": 123456,
    "created_by": "xxx@gmail.com",
    "tags": ["xxxxx"]
}]

C# 代码:

var jsonString = (File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(),"delete_result.json")));
var objects = JsonConvert.DeserializeObject<dynamic>(jsonString);
foreach(var o in objects)
{
    Console.WriteLine($"{o.id.ToString()}");
}

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