如何在 ASP.NET 中将 JSON 反序列化为简单的 Dictionary<string,string>?

Posted

技术标签:

【中文标题】如何在 ASP.NET 中将 JSON 反序列化为简单的 Dictionary<string,string>?【英文标题】:How can I deserialize JSON to a simple Dictionary<string,string> in ASP.NET? 【发布时间】:2021-11-19 21:16:24 【问题描述】:

我有一个简单的 JSON 键/值列表,通过 POST 发送回 ASP.NET。示例:

 "key1": "value1", "key2": "value2"

我不想反序列化为强类型的 .NET 对象

我只需要一个普通的旧 Dictionary(Of String, String),或一些等价物(哈希表、Dictionary(Of String, Object)、老式 StringDictionary--hell、a 2- D 字符串数组对我有用。

我可以使用 ASP.NET 3.5 中可用的任何东西,以及流行的 Json.NET(我已经将其用于序列化客户端)。

显然,这些 JSON 库都没有这种开箱即用的明显能力——它们完全专注于通过强合约实现基于反射的反序列化。

有什么想法吗?

限制:

    我不想实现自己的 JSON 解析器 还不能使用 ASP.NET 4.0 希望远离旧的、已弃用的 JSON ASP.NET 类

【问题讨论】:

re:限制 3,javascriptSerizlizer 在 ASP.NET MVC 中使用,不再被弃用。 很难找到一种简单的方法将 json 字符串转换为我可以轻松使用的东西,而无需翻阅许多不同的 ***。在其他语言中这很容易,但 Java 和 C# 似乎不遗余力地让生活变得困难。 【参考方案1】:

Json.NET 这样做...

string json = @"""key1"":""value1"",""key2"":""value2""";

var values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);

更多示例:Serializing Collections with Json.NET

【讨论】:

当您的值是整数时,这是否也有效。它们会自动转换为“字符串”吗? @Highmastdon 不,它没有。我发现反序列化为字典的最佳方法是使用 dynamic 作为值的类型:JsonConvert.DeserializeObject&lt;Dictionary&lt;string, dynamic&gt;&gt;(json); 在这个页面上用一个非常混乱的键/值对尝试了几个答案,而 JSON.NET 是我尝试过的唯一一个有效的方法。 如果您在 json [key: "a", value: "1", key: "b", value:"2"] 中使用键值对数组则不起作用,您必须执行以下操作:var dict = JsonConvert.DeserializeObject&lt;List&lt;KeyValuePair&lt;string, string&gt;&gt;&gt;(json); 如果值是嵌套对象也不起作用,因为 json.net 将它们创建为 JObjects【参考方案2】:

我确实发现 .NET 有一种内置方法,可以通过 3.5 System.Web.Extensions 程序集中的 System.Web.Script.Serialization.JavaScriptSerializer 类型将 JSON 字符串转换为 Dictionary&lt;String, Object&gt;。使用方法DeserializeObject(String)

在将内容类型为“application/json”的 ajax 发布(通过 jquery)发送到静态 .net 页面方法时,我偶然发现了这一点,并看到该方法(具有 Object 类型的单个参数)神奇地收到了这本词典。

【讨论】:

但内置的 javascriptserializer 比 json.net 更错误,该解决方案更好。例如,javascriptseralizer 将返回空值而不是空白字符串,并且对于可为空的属性根本不起作用,等等。 @pilavdzice 更不用说你在尝试解析日期时的乐趣了,因为它假定 MS 的非标准日期格式。 快速代码示例:var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); 后跟 Dictionary&lt;string, object&gt; dict = (Dictionary&lt;string, object&gt;)jsSerializer.DeserializeObject(jsonString); Nate Cook 示例在一个简单案例中的优势是避免了对外部 DLL 的需要。我正在从只能依赖 .Net 框架的独立控制台访问 API。【参考方案3】:

对于那些在互联网上搜索并偶然发现这篇文章的人,我写了一篇关于如何使用 JavaScriptSerializer 类的博客文章。

阅读更多... http://procbits.com/2011/04/21/quick-json-serializationdeserialization-in-c/

这是一个例子:

var json = "\"id\":\"13\", \"value\": true";
var jss = new JavaScriptSerializer();
var table = jss.Deserialize<dynamic>(json);
Console.WriteLine(table["id"]);
Console.WriteLine(table["value"]);

【讨论】:

嗯,我已经尝试过你的解决方案...我有这样的 json "id":"13", "value": true 对我来说只有 Dictionary 解决方案有效 好的,我发现问题出在哪里...您需要在字典声明后添加 [] 才能正确反序列化...我也在为您的博客文章添加评论...干杯;) 我已更新我的答案以反映您的特定数据集。它适用于动态。 我刚刚写了另一个更灵活且支持 Silverlight 的 JSON 解析器:procbits.com/2011/08/11/…【参考方案4】:

我遇到了同样的问题,所以我自己写了这个。该解决方案与其他答案不同,因为它可以反序列化到多个级别。

只需将 JSON 字符串发送到 deserializeToDictionary 函数,它将返回非强类型 Dictionary&lt;string, object&gt; 对象。

旧代码

private Dictionary<string, object> deserializeToDictionary(string jo)

    var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo);
    var values2 = new Dictionary<string, object>();
    foreach (KeyValuePair<string, object> d in values)
    
        // if (d.Value.GetType().FullName.Contains("Newtonsoft.Json.Linq.JObject"))
        if (d.Value is JObject)
        
            values2.Add(d.Key, deserializeToDictionary(d.Value.ToString()));
        
        else
        
            values2.Add(d.Key, d.Value);
        
    
    return values2;

例如:这将返回 Facebook JSON 响应的 Dictionary&lt;string, object&gt; 对象。

测试

private void button1_Click(object sender, EventArgs e)

    string responsestring = "\"id\":\"721055828\",\"name\":\"Dasun Sameera Weerasinghe\",\"first_name\":\"Dasun\",\"middle_name\":\"Sameera\",\"last_name\":\"Weerasinghe\",\"username\":\"dasun\",\"gender\":\"male\",\"locale\":\"en_US\",  hometown: id: \"108388329191258\", name: \"Moratuwa, Sri Lanka\",";
    Dictionary<string, object> values = deserializeToDictionary(responsestring);

注意:家乡进一步脱毒成Dictionary&lt;string, object&gt; 对象。

更新

如果 JSON 字符串上没有数组,我的旧答案效果很好。如果元素是数组,则此进一步反序列化为 List&lt;object&gt;

只需将 JSON 字符串发送到 deserializeToDictionaryOrList 函数,它将返回非强类型 Dictionary&lt;string, object&gt; 对象或 @987654328 @

private static object deserializeToDictionaryOrList(string jo,bool isArray=false)

    if (!isArray)
    
        isArray = jo.Substring(0, 1) == "[";
    
    if (!isArray)
    
        var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo);
        var values2 = new Dictionary<string, object>();
        foreach (KeyValuePair<string, object> d in values)
        
            if (d.Value is JObject)
            
                values2.Add(d.Key, deserializeToDictionary(d.Value.ToString()));
            
            else if (d.Value is JArray)
            
                values2.Add(d.Key, deserializeToDictionary(d.Value.ToString(), true));
            
            else
            
                values2.Add(d.Key, d.Value);
            
        
        return values2;
    else
    
        var values = JsonConvert.DeserializeObject<List<object>>(jo);
        var values2 = new List<object>();
        foreach (var d in values)
        
            if (d is JObject)
            
                values2.Add(deserializeToDictionary(d.ToString()));
            
            else if (d is JArray)
            
                values2.Add(deserializeToDictionary(d.ToString(), true));
            
            else
            
                values2.Add(d);
            
        
        return values2;
    

【讨论】:

@Jordan 感谢您指出,我对这段代码做了一些修改,但我现在没有。此代码不处理 JArray 对象,我将在获得后更新代码。 没问题。我之所以提到它,是因为了解 isas 运算符对我有很大帮助并简化了我自己的代码。 它可以工作,但效率不高,因为它调用 ToString 然后再次反序列化。看看下面法尔科的回答。它只对源字符串进行一次反序列化。 Falko 的答案只有在您事先知道数据结构的情况下才有效。此解决方案可用于任何 JSON 字符串。【参考方案5】:

尝试不使用任何外部 JSON 实现,所以我像这样反序列化:

string json = "\"id\":\"13\", \"value\": true";

var serializer = new JavaScriptSerializer(); //using System.Web.Script.Serialization;

Dictionary<string, string> values = serializer.Deserialize<Dictionary<string, string>>(json);

【讨论】:

添加引用 System.Web.Extensions 以使用 System.Web.Script 我最喜欢这个答案,因为它很简单并且使用 .NET System.Web.Script.Serialization。它只是工作。我什至可以使用像 string json = "'id':13, 'value': true"; 这样的“无效”JSON。【参考方案6】:

System.Text.Json

现在可以使用 .NET Core 3.0 内置的 System.Text.Json 来完成。现在可以在不使用第三方库的情况下反序列化 JSON。

var json = @"""key1"":""value1"",""key2"":""value2""";
var values = JsonSerializer.Deserialize<Dictionary<string, string>>(json);

如果使用 .NET Standard 或 .NET Framework,也可以在 NuGet 包 System.Text.Json 中使用。


请务必阅读并理解:

https://github.com/dotnet/runtime/issues/30452

【讨论】:

是的! System.Text.Json 是这些天要走的路。 是的,它看起来很有希望!但是,请注意 .NET Core 3.1 的默认版本 System.Text.Json 不支持使用非字符串键反序列化字典。虽然我的 OP 是关于字符串的,但现在在实践中,我确实有很多 Guid 键,所以在尝试进行切换时这“有点”我。它也没有某些属性(必需等)的等价物。 不错的答案,但 .NET json 函数的来源是 Newtonsoft.Json; 如果你有嵌套数据,我推荐Dictionary&lt;string, JsonElement&gt; 这样你就可以使用childNode.Value.ValueKind == JsonValueKind.Object 测试对象值并使用JsonSerializer.Deserialize&lt;Dictionary&lt;string, JsonElement&gt;&gt;(childNode.Value); 再次反序列化【参考方案7】:

我只需要解析一个嵌套字典,比如


    "x": 
        "a": 1,
        "b": 2,
        "c": 3
    

JsonConvert.DeserializeObject 没有帮助。我找到了以下方法:

var dict = JObject.Parse(json).SelectToken("x").ToObject<Dictionary<string, int>>();

SelectToken 可让您深入挖掘所需的字段。您甚至可以指定像 "x.y.z" 这样的路径来进一步深入 JSON 对象。

【讨论】:

JObject.Parse(json).ToObject>>() 在我的场景中为我工作谢谢【参考方案8】:

如果您追求一种轻量级、不添加引用的方法,也许我刚刚编写的这段代码会起作用(但我不能 100% 保证稳健性)。

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

public Dictionary<string, object> ParseJSON(string json)

    int end;
    return ParseJSON(json, 0, out end);

private Dictionary<string, object> ParseJSON(string json, int start, out int end)

    Dictionary<string, object> dict = new Dictionary<string, object>();
    bool escbegin = false;
    bool escend = false;
    bool inquotes = false;
    string key = null;
    int cend;
    StringBuilder sb = new StringBuilder();
    Dictionary<string, object> child = null;
    List<object> arraylist = null;
    Regex regex = new Regex(@"\\u([0-9a-z]4)", RegexOptions.IgnoreCase);
    int autoKey = 0;
    for (int i = start; i < json.Length; i++)
    
        char c = json[i];
        if (c == '\\') escbegin = !escbegin;
        if (!escbegin)
        
            if (c == '"')
            
                inquotes = !inquotes;
                if (!inquotes && arraylist != null)
                
                    arraylist.Add(DecodeString(regex, sb.ToString()));
                    sb.Length = 0;
                
                continue;
            
            if (!inquotes)
            
                switch (c)
                
                    case '':
                        if (i != start)
                        
                            child = ParseJSON(json, i, out cend);
                            if (arraylist != null) arraylist.Add(child);
                            else
                            
                                dict.Add(key, child);
                                key = null;
                            
                            i = cend;
                        
                        continue;
                    case '':
                        end = i;
                        if (key != null)
                        
                            if (arraylist != null) dict.Add(key, arraylist);
                            else dict.Add(key, DecodeString(regex, sb.ToString()));
                        
                        return dict;
                    case '[':
                        arraylist = new List<object>();
                        continue;
                    case ']':
                        if (key == null)
                        
                            key = "array" + autoKey.ToString();
                            autoKey++;
                        
                        if (arraylist != null && sb.Length > 0)
                        
                            arraylist.Add(sb.ToString());
                            sb.Length = 0;
                        
                        dict.Add(key, arraylist);
                        arraylist = null;
                        key = null;
                        continue;
                    case ',':
                        if (arraylist == null && key != null)
                        
                            dict.Add(key, DecodeString(regex, sb.ToString()));
                            key = null;
                            sb.Length = 0;
                        
                        if (arraylist != null && sb.Length > 0)
                        
                            arraylist.Add(sb.ToString());
                            sb.Length = 0;
                        
                       continue;
                    case ':':
                        key = DecodeString(regex, sb.ToString());
                        sb.Length = 0;
                        continue;
                
            
        
        sb.Append(c);
        if (escend) escbegin = false;
        if (escbegin) escend = true;
        else escend = false;
    
    end = json.Length - 1;
    return dict; //theoretically shouldn't ever get here

private string DecodeString(Regex regex, string str)

    return Regex.Unescape(regex.Replace(str, match => char.ConvertFromUtf32(Int32.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber))));

[我意识到这违反了 OP 限制 #1,但从技术上讲,不是你写的,而是我写的]

【讨论】:

这是唯一适用于 Silverlight 且无需依赖的答案! Silverlight 没有 JavascriptSerializer 或 Serializable。没有依赖意味着没有 Json.NET、RestSharp 或 MiniJSON。只有@DanCsharpster 尝试了另一种可能的解决方案,但不幸的是它不像这个那样对我有用。 添加对像 JSON.NET 这样简单的东西的引用有什么问题?它是否需要如此轻巧以至于您无法引用任何内容?我并不是说你的代码不起作用,但无论何时你自己推出,你显然都会冒着代码不那么健壮的风险,比如边缘情况,或者像 JSON.NET 这样的经过测试的库。 当你有一个好的选择时,自己动手是个坏主意。我知道没有任何情况必须是 轻量级的。而且我宁愿使用易于阅读和更改的不太优化的代码。 我最初写那段代码是因为我别无选择。考虑像 Silverlight 或各种类型的 Office 产品提供商,在这些情况下,向项目添加外部引用要么非常有问题,要么不可能。 几年后我就知道了,但这仍然是一个非常有效的问题。对于任何想知道为什么我们想要如此轻量级的人,好吧,如果您使用 SQL CLR C#,那么您可以使用的“安全”库只有这么多,而 System.RunTime.Serialization 不是其中之一,不幸的是 JSON。 NET 依赖于它,因此它也不能使用。感谢 dexy 的出色工作,我敢于对其进行一些改进,以便能够反序列化数组的数组,see the updated code in my answer here。【参考方案9】:

Mark Rendle 发布了此 as a comment,我想将其发布为答案,因为它是迄今为止唯一有效的解决方案,可以返回成功,并且错误代码 json 结果来自 Google reCaptcha 响应。

string jsonReponseString= wClient.DownloadString(requestUrl);    
IDictionary<string, object> dict = new JavaScriptSerializer().DeserializeObject(jsonReponseString) as IDictionary<string, object>;

再次感谢,马克!

【讨论】:

JavaScriptSerializer 几乎已被弃用。文档说我们应该使用 JSON.NET (docs.microsoft.com/en-us/dotnet/api/…) 也适用于您不想包含其他依赖项的旧版网络表单应用程序。【参考方案10】:

我在jSnake04和Dasun提交的代码上添加了这里。我添加了代码来从JArray 实例创建对象列表。它具有双向递归,但由于它在固定的有限树模型上运行,因此除非数据量很大,否则不会出现堆栈溢出的风险。

/// <summary>
/// Deserialize the given JSON string data (<paramref name="data"/>) into a
///   dictionary.
/// </summary>
/// <param name="data">JSON string.</param>
/// <returns>Deserialized dictionary.</returns>
private IDictionary<string, object> DeserializeData(string data)

    var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(data);

    return DeserializeData(values);


/// <summary>
/// Deserialize the given JSON object (<paramref name="data"/>) into a dictionary.
/// </summary>
/// <param name="data">JSON object.</param>
/// <returns>Deserialized dictionary.</returns>
private IDictionary<string, object> DeserializeData(JObject data)

    var dict = data.ToObject<Dictionary<String, Object>>();

    return DeserializeData(dict);


/// <summary>
/// Deserialize any elements of the given data dictionary (<paramref name="data"/>) 
///   that are JSON object or JSON arrays into dictionaries or lists respectively.
/// </summary>
/// <param name="data">Data dictionary.</param>
/// <returns>Deserialized dictionary.</returns>
private IDictionary<string, object> DeserializeData(IDictionary<string, object> data)

    foreach (var key in data.Keys.ToArray()) 
    
        var value = data[key];

        if (value is JObject)
            data[key] = DeserializeData(value as JObject);

        if (value is JArray)
            data[key] = DeserializeData(value as JArray);
    

    return data;


/// <summary>
/// Deserialize the given JSON array (<paramref name="data"/>) into a list.
/// </summary>
/// <param name="data">Data dictionary.</param>
/// <returns>Deserialized list.</returns>
private IList<Object> DeserializeData(JArray data)

    var list = data.ToObject<List<Object>>();

    for (int i = 0; i < list.Count; i++)
    
        var value = list[i];

        if (value is JObject)
            list[i] = DeserializeData(value as JObject);

        if (value is JArray)
            list[i] = DeserializeData(value as JArray);
    

    return list;

【讨论】:

【参考方案11】:

对于任何试图将 JSON 转换为字典只是为了从中检索一些值的人。有a simple way使用Newtonsoft.JSON

using Newtonsoft.Json.Linq
...

JObject o = JObject.Parse(@"
  'CPU': 'Intel',
  'Drives': [
    'DVD read/writer',
    '500 gigabyte hard drive'
  ]
");

string cpu = (string)o["CPU"];
// Intel

string firstDrive = (string)o["Drives"][0];
// DVD read/writer

IList<string> allDrives = o["Drives"].Select(t => (string)t).ToList();
// DVD read/writer
// 500 gigabyte hard drive

【讨论】:

【参考方案12】:

编辑:这可行,但使用 Json.NET 的公认答案要简单得多。留下这个以防有人需要纯 BCL 代码。

开箱即用的 .NET 框架不支持它。一个明显的疏忽——并不是每个人都需要反序列化为具有命名属性的对象。所以我最终推出了自己的:

VB.NET:

<Serializable()> Public Class StringStringDictionary
    Implements ISerializable
    Public dict As System.Collections.Generic.Dictionary(Of String, String)
    Public Sub New()
        dict = New System.Collections.Generic.Dictionary(Of String, String)
    End Sub
    Protected Sub New(info As SerializationInfo, _
          context As StreamingContext)
        dict = New System.Collections.Generic.Dictionary(Of String, String)
        For Each entry As SerializationEntry In info
            dict.Add(entry.Name, DirectCast(entry.Value, String))
        Next
    End Sub
    Public Sub GetObjectData(info As SerializationInfo, context As StreamingContext) Implements ISerializable.GetObjectData
        For Each key As String in dict.Keys
            info.AddValue(key, dict.Item(key))
        Next
    End Sub
End Class

在 C# 上也是这样:

public class StringStringDictionary : ISerializable

    public System.Collections.Generic.Dictionary<string, string> dict;
    public StringStringDictionary()
    
        dict = new System.Collections.Generic.Dictionary<string, string>();
    
    protected StringStringDictionary(SerializationInfo info, StreamingContext context)
    
        dict = new System.Collections.Generic.Dictionary<string, string>();
        foreach (SerializationEntry entry in info)
            dict.Add(entry.Name, (string)entry.Value);
    
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    
        foreach (string key in dict.Keys)
            info.AddValue(key, dict[key]);
    

调用:

string MyJsonString = " \"key1\": \"value1\", \"key2\": \"value2\"";
System.Runtime.Serialization.Json.DataContractJsonSerializer dcjs = new
  System.Runtime.Serialization.Json.DataContractJsonSerializer(
    typeof(StringStringDictionary));
System.IO.MemoryStream ms = new
  System.IO.MemoryStream(Encoding.UTF8.GetBytes(MyJsonString));
StringStringDictionary myfields = (StringStringDictionary)dcjs.ReadObject(ms);
Response.Write("Value of key2: " + myfields.dict["key2"]);

对于 C# 和 VB.NET 的混合感到抱歉……

【讨论】:

[TestMethod] public void TestSimpleObject() const string json = @"""Name"":""Bob"",""Age"":42"; var dict = new JavaScriptSerializer().DeserializeObject(json) as IDictionary;断言.IsNotNull(dict); Assert.IsTrue(dict.ContainsKey("Name")); Assert.AreEqual("Bob", dict["Name"]); Assert.IsTrue(dict.ContainsKey("Age")); Assert.AreEqual(42, dict["Age"]); 这太棒了。帮助使用 JSON 与基于浏览器的客户端进行交互的 WCF 服务实现。 @Mark Rendle:您的实现非常简单,并且是迄今为止唯一对我有用的实现成功和错误代码 json 结果的方法。我已经尝试了很多解决方案,所以感谢您将其发布为评论。这应该是答案。 不起作用(至少在.NetFramework 4.6.1)://因为num of char。在评论限制下,我将发布一些 1) 已发布(类仅继承 ISerializable) - 出现异常:"InvalidDataContractException: Type 'StringStringDictionary' cannot be serialized. 考虑用DataContractAttribute 属性,并使用 DataMemberAttribute 属性标记您想要序列化的所有成员。或者,您可以确保该类型是公共的并且具有无参数的构造函数 - 然后将序列化该类型的所有公共成员,并且不需要任何属性。” 2) 作为类及其 dict 字段已经公开,尝试添加 [DataContract][DataMember] 作为建议 - 出现异常:“系统.Runtime.Serialization.InvalidDataContractException: ISerializable type 'Translator.Utils.JsonReader+StringStringDictionary' cannot have DataContract." 3) 好的,试试[DataContract] & [DataMember] 属性(w /o ISerializable 继承) - ReadObject 现在完成了没有异常,但实际上并没有解析:myfields.dict == null【参考方案13】:

我在另一个答案中添加了对 JSON 中空值的检查

我有同样的问题,所以我自己写了这个。这个解决方案是 与其他答案不同,因为它可以反序列化为 多个级别。

只需将 json 字符串发送到 deserializeToDictionary 函数即可 将返回非强类型 Dictionary&lt;string, object&gt; 对象。

private Dictionary<string, object> deserializeToDictionary(string jo)

    var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo);
    var values2 = new Dictionary<string, object>();
    foreach (KeyValuePair<string, object> d in values)
    
        if (d.Value != null && d.Value.GetType().FullName.Contains("Newtonsoft.Json.Linq.JObject"))
        
            values2.Add(d.Key, deserializeToDictionary(d.Value.ToString()));
        
        else
        
            values2.Add(d.Key, d.Value);
        
    
    return values2;

例如:这将返回 Facebook 的 Dictionary&lt;string, object&gt; 对象 JSON 响应。

private void button1_Click(object sender, EventArgs e)

    string responsestring = "\"id\":\"721055828\",\"name\":\"Dasun Sameera
        Weerasinghe\",\"first_name\":\"Dasun\",\"middle_name\":\"Sameera\",\"last_name\":\"Weerasinghe\",\"username\":\"dasun\",\"gender\":\"male\",\"locale\":\"en_US\",
        hometown: id: \"108388329191258\", name: \"Moratuwa, Sri Lanka\",";
    Dictionary<string, object> values = deserializeToDictionary(responsestring);

注意:hometown 进一步反序列化为Dictionary&lt;string, object&gt; 对象。

【讨论】:

+1 正如我在上面对 Dasun 所说的那样。你可以检查是否d.Value is JObject。您不必通过反射来检查类型。使用is 运算符,您无需检查是否为空。如果对象为 null,则返回 false。【参考方案14】:

似乎所有这些答案都只是假设你可以从一个更大的对象中得到那个小字符串...... 987654323@DataContract系统,这里有一个解决方案:

An answer on gis.stackexchange.com 拥有this interesting link。我不得不通过archive.org 恢复它,但它提供了一个非常完美的解决方案:一个自定义的IDataContractSurrogate 类,您可以在其中完全实现自己的类型。我能够轻松地扩展它。

不过,我对它进行了很多更改。由于原始来源不再可用,我将在此处发布整个课程:

using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;

namespace JsonTools

    /// <summary>
    /// Allows using Dictionary&lt;String,String&gt; and Dictionary&lt;String,Boolean&gt; types, and any others you'd like to add.
    /// Source: https://web.archive.org/web/20100317222656/my6solutions.com/post/2009/06/30/DataContractSerializer-DataContractJsonSerializer-JavaScriptSerializer-XmlSerializer-for-serialization.aspx
    /// </summary>
    public class JsonSurrogate : IDataContractSurrogate
    
        /// <summary>
        /// Deserialize an object with added support for the types defined in this class.
        /// </summary>
        /// <typeparam name="T">Contract class</typeparam>
        /// <param name="json">JSON String</param>
        /// <param name="encoding">Text encoding</param>
        /// <returns>The deserialized object of type T</returns>
        public static T Deserialize<T>(String json, Encoding encoding)
        
            if (encoding == null)
                encoding = new UTF8Encoding(false);
            DataContractJsonSerializer deserializer = new DataContractJsonSerializer(
                typeof(T), new Type[0], int.MaxValue, true, new JsonSurrogate(), false);
            using (MemoryStream stream = new MemoryStream(encoding.GetBytes(json)))
            
                T result = (T)deserializer.ReadObject(stream);
                return result;
            
        

        // make sure all values in this are classes implementing JsonSurrogateObject.
        private static Dictionary<Type, Type> KnownTypes = 
            new Dictionary<Type, Type>()
            
                typeof(Dictionary<String, String>), typeof(SSDictionary),
                typeof(Dictionary<String, Boolean>), typeof(SBDictionary)
            ;

        #region Implemented surrogate dictionary classes

        [Serializable]
        public class SSDictionary : SurrogateDictionary<String>
        
            public SSDictionary() : base() 
            protected SSDictionary (SerializationInfo info, StreamingContext context) : base(info, context) 
        
        [Serializable]
        public class SBDictionary : SurrogateDictionary<Boolean>
        
            public SBDictionary() : base() 
            protected SBDictionary (SerializationInfo info, StreamingContext context) : base(info, context) 
        

        #endregion

        /// <summary>Small interface to easily extract the final value from the object.</summary>
        public interface JsonSurrogateObject
        
            Object DeserializedObject  get; 
        

        /// <summary>
        /// Class for deserializing any simple dictionary types with a string as key.
        /// </summary>
        /// <typeparam name="T">Any simple type that will be deserialized correctly.</typeparam>
            [Serializable]
        public abstract class SurrogateDictionary<T> : ISerializable, JsonSurrogateObject
        
            public Object DeserializedObject  get  return dict;  
            private Dictionary<String, T> dict;

            public SurrogateDictionary()
            
                dict = new Dictionary<String, T>();
            

            // deserialize
            protected SurrogateDictionary(SerializationInfo info, StreamingContext context)
            
                dict = new Dictionary<String, T>();
                foreach (SerializationEntry entry in info)
                
                    // This cast will only work for base types, of course.
                    dict.Add(entry.Name, (T)entry.Value);
                
            
            // serialize
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            
                foreach (String key in dict.Keys)
                
                    info.AddValue(key, dict[key]);
                
            

        

        /// <summary>
            /// Uses the KnownTypes dictionary to get the surrogate classes.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public Type GetDataContractType(Type type)
        
            Type returnType;
            if (KnownTypes.TryGetValue(type, out returnType))
            
                return returnType;
            
            return type;
        

        public object GetObjectToSerialize(object obj, Type targetType)
        
            throw new NotImplementedException();
        

        /// <summary>
        ///     Gets the object out of the surrogate datacontract object. This function is the reason all surrogate objects need to implement the JsonSurrogateObject class.
        /// </summary>
        /// <param name="obj">Result of the deserialization</param>
        /// <param name="targetType">Expected target type of the deserialization</param>
        /// <returns></returns>
        public object GetDeserializedObject(object obj, Type targetType)
        
            if (obj is JsonSurrogateObject)
            
                return ((JsonSurrogateObject)obj).DeserializedObject;
            
            return obj;
        

        public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        
            return null;
        

        #region not implemented

        public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
        
            throw new NotImplementedException();
        

        public object GetCustomDataToExport(Type clrType, Type dataContractType)
        
            throw new NotImplementedException();
        

        public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
        
            throw new NotImplementedException();
        

        public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
        
            throw new NotImplementedException();
        

        #endregion
    

要向类添加新的受支持类型,您只需要添加您的类,为其提供正确的构造函数和函数(以 SurrogateDictionary 为例),确保它继承 JsonSurrogateObject,并添加其类型映射到KnownTypes 字典。包含的 SurrogateDictionary 可以作为任何 Dictionary&lt;String,T&gt; 类型的基础,其中 T 是可以正确反序列化的任何类型。

调用它真的很简单:

MyObjtype newObj = JsonSurrogate.Deserialize<MyObjtype>(jsonStr, encoding);

请注意,由于某种原因,这个东西在使用包含空格的键字符串时遇到了麻烦;他们根本没有出现在最终名单中。请注意,这可能只是违反了 json 规范,而且我调用的 api 实现得很差;我不知道。无论如何,我通过在原始 json 数据中用下划线替换它们并在反序列化后修复字典来解决这个问题。

【讨论】:

顺便说一句,由于某些特殊原因,Mono 似乎无法运行这些东西... 感谢分享,可惜这个方案不支持非原始类型,也无法获取原始值,所以可以自己构造。如果我在 KnownTypes 中注册我的自定义类型并在字典中使用它,它首先调用字典,我希望它从最远的类型开始从下到上解析,到更复杂的类型。 好吧,这个问题只问了Dictionary&lt;String,String&gt;。老实说,我从来没有尝试过用这个系统反序列化复杂类型。【参考方案15】:

基于cmetsabove试试JsonConvert.DeserializeObject&lt;Dictionary&lt;string,dynamic&gt;&gt;(json)

var json = @"""key1"":1,""key2"":""value2"", ""object1"":""property1"":""value1"",""property2"":[2,3,4,5,6,7]";
var parsedObject = JsonConvert.DeserializeObject<Dictionary<string,dynamic>>(json);

似乎甚至适用于复杂的对象和列表。

【讨论】:

【参考方案16】:

我刚刚在RestSharp 中实现了这个。 This post 对我很有帮助。

除了链接中的代码,这是我的代码。现在,当我执行以下操作时,我会得到Dictionary 的结果:

var jsonClient = new RestClient(url.Host);
jsonClient.AddHandler("application/json", new DynamicJsonDeserializer());
var jsonRequest = new RestRequest(url.Query, Method.GET);
Dictionary<string, dynamic> response = jsonClient.Execute<JObject>(jsonRequest).Data.ToObject<Dictionary<string, dynamic>>();

请注意您所期望的 JSON 类型 - 在我的例子中,我正在检索具有多个属性的单个对象。在附加的链接中,作者正在检索一个列表。

【讨论】:

【参考方案17】:

我的方法直接反序列化为 IDictionary,中间没有 JObject 或 ExpandObject。代码使用转换器,基本上是从 JSON.NET 源代码中的 ExpandoObjectConverter 类复制而来,但使用 IDictionary 而不是 ExpandoObject。

用法:

var settings = new JsonSerializerSettings()

    Converters =  new DictionaryConverter() ,
;
var result = JsonConvert.DeserializeObject<IDictionary<string, object>>(json, settings);

代码:

// based on ExpandoObjectConverter, but using arrays instead of IList, to behave similar to System.Web.Script.Serialization.JavaScriptSerializer
public class DictionaryConverter : JsonConverter

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    
    

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    
        return ReadValue(reader);
    

    public override bool CanConvert(Type objectType)
    
        return (objectType == typeof(IDictionary<string, object>));
    

    public override bool CanWrite
    
        get  return false; 
    

    private object ReadValue(JsonReader reader)
    
        while (reader.TokenType == JsonToken.Comment)
        
            if (!reader.Read())
                throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");
        

        switch (reader.TokenType)
        
            case JsonToken.StartObject:
                return ReadObject(reader);
            case JsonToken.StartArray:
                return ReadList(reader);
            default:
                if (IsPrimitiveToken(reader.TokenType))
                    return reader.Value;

                throw JsonSerializationExceptionCreate(reader, string.Format(CultureInfo.InvariantCulture, "Unexpected token when converting IDictionary<string, object>: 0", reader.TokenType));
        
    

    private object ReadList(JsonReader reader)
    
        List<object> list = new List<object>();

        while (reader.Read())
        
            switch (reader.TokenType)
            
                case JsonToken.Comment:
                    break;
                default:
                    object v = ReadValue(reader);

                    list.Add(v);
                    break;
                case JsonToken.EndArray:
                    return list;
            
        

        throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");
    

    private object ReadObject(JsonReader reader)
    
        IDictionary<string, object> dictionary = new Dictionary<string, object>();
        while (reader.Read())
        
            switch (reader.TokenType)
            
                case JsonToken.PropertyName:
                    string propertyName = reader.Value.ToString();

                    if (!reader.Read())
                        throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");

                    object v = ReadValue(reader);

                    dictionary[propertyName] = v;
                    break;
                case JsonToken.Comment:
                    break;
                case JsonToken.EndObject:
                    return dictionary;
            
        

        throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");
    

    //based on internal Newtonsoft.Json.JsonReader.IsPrimitiveToken
    internal static bool IsPrimitiveToken(JsonToken token)
    
        switch (token)
        
            case JsonToken.Integer:
            case JsonToken.Float:
            case JsonToken.String:
            case JsonToken.Boolean:
            case JsonToken.Undefined:
            case JsonToken.Null:
            case JsonToken.Date:
            case JsonToken.Bytes:
                return true;
            default:
                return false;
        
    

    // based on internal Newtonsoft.Json.JsonSerializationException.Create
    private static JsonSerializationException JsonSerializationExceptionCreate(JsonReader reader, string message, Exception ex = null)
    
        return JsonSerializationExceptionCreate(reader as IJsonLineInfo, reader.Path, message, ex);
    

    // based on internal Newtonsoft.Json.JsonSerializationException.Create
    private static JsonSerializationException JsonSerializationExceptionCreate(IJsonLineInfo lineInfo, string path, string message, Exception ex)
    
        message = JsonPositionFormatMessage(lineInfo, path, message);

        return new JsonSerializationException(message, ex);
    

    // based on internal Newtonsoft.Json.JsonPosition.FormatMessage
    internal static string JsonPositionFormatMessage(IJsonLineInfo lineInfo, string path, string message)
    
        if (!message.EndsWith(Environment.NewLine))
        
            message = message.Trim();

            if (!message.EndsWith(".", StringComparison.Ordinal))
                message += ".";

            message += " ";
        

        message += string.Format(CultureInfo.InvariantCulture, "Path '0'", path);

        if (lineInfo != null && lineInfo.HasLineInfo())
            message += string.Format(CultureInfo.InvariantCulture, ", line 0, position 1", lineInfo.LineNumber, lineInfo.LinePosition);

        message += ".";

        return message;
    

【讨论】:

【参考方案18】:

游戏有点晚了,但上述解决方案都没有为我指明纯粹简单的 .NET 方向,没有 json.net 解决方案。所以就在这里,最终变得非常简单。下面是如何使用标准 .NET Json 序列化完成的完整运行示例,该示例在根对象和子对象中都有字典。

金子弹是这只猫,将设置解析为序列化程序的第二个参数:

DataContractJsonSerializerSettings settings =
                       new DataContractJsonSerializerSettings();
                    settings.UseSimpleDictionaryFormat = true;

完整代码如下:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

    namespace Kipon.dk
    
        public class JsonTest
        
            public const string EXAMPLE = @"
                ""id"": ""some id"",
                ""children"": 
                ""f1"": 
                    ""name"": ""name 1"",
                    ""subs"": 
                    ""1"":  ""name"": ""first sub"" ,
                    ""2"":  ""name"": ""second sub"" 
                    
                ,
                ""f2"": 
                    ""name"": ""name 2"",
                    ""subs"": 
                    ""37"":  ""name"":  ""is 37 in key""
                    
                
                
            
            ";

            [DataContract]
            public class Root
            
                [DataMember(Name ="id")]
                public string Id  get; set; 

                [DataMember(Name = "children")]
                public Dictionary<string,Child> Children  get; set; 
            

            [DataContract]
            public class Child
            
                [DataMember(Name = "name")]
                public string Name  get; set; 

                [DataMember(Name = "subs")]
                public Dictionary<int, Sub> Subs  get; set; 
            

            [DataContract]
            public class Sub
            
                [DataMember(Name = "name")]
                public string Name  get; set; 
            

            public static void Test()
            
                var array = System.Text.Encoding.UTF8.GetBytes(EXAMPLE);
                using (var mem = new System.IO.MemoryStream(array))
                
                    mem.Seek(0, System.IO.SeekOrigin.Begin);
                    DataContractJsonSerializerSettings settings =
                       new DataContractJsonSerializerSettings();
                    settings.UseSimpleDictionaryFormat = true;

                    var ser = new DataContractJsonSerializer(typeof(Root), settings);
                    var data = (Root)ser.ReadObject(mem);
                    Console.WriteLine(data.Id);
                    foreach (var childKey in data.Children.Keys)
                    
                        var child = data.Children[childKey];
                        Console.WriteLine(" Child: " + childKey + " " + child.Name);
                        foreach (var subKey in child.Subs.Keys)
                        
                            var sub = child.Subs[subKey];
                            Console.WriteLine("   Sub: " + subKey + " " + sub.Name);
                        
                    
                
            
        
    

【讨论】:

【参考方案19】:

很烦人的是,如果你想使用默认的模型绑定器,看起来你将不得不使用像 POST 这样的数字索引值。

请参阅本文http://msdn.microsoft.com/en-us/magazine/hh781022.aspx的以下摘录:

虽然有点违反直觉,但 JSON 请求具有相同的 要求——它们也必须遵守表单后命名语法。 以之前的 UnitPrice 的 JSON 有效负载为例 收藏。此数据的纯 JSON 数组语法为 表示为:

[ 
   "Code": "USD", "Amount": 100.00 ,
   "Code": "EUR", "Amount": 73.64 
]

但是,默认值提供程序和模型绑定器需要 要表示为 JSON 表单帖子的数据:


  "UnitPrice[0].Code": "USD",
  "UnitPrice[0].Amount": 100.00,

  "UnitPrice[1].Code": "EUR",
  "UnitPrice[1].Amount": 73.64

复杂的对象收集场景可能是最复杂的场景之一 开发人员遇到的广泛存在问题的场景,因为 语法不一定对所有开发人员都很明显。然而,一旦你 学习发布复杂集合的相对简单的语法, 这些场景变得更容易处理。

【讨论】:

【参考方案20】:

你可以使用Tiny-JSON

string json = "\"key1\":\"value1\", \"key2\":\"value2\"";
IDictionary<string, string> dict = Tiny.Json.Decode<Dictionary<string, string>>(json);

【讨论】:

【参考方案21】:

我建议使用 .NET 4.5 中的 System.Runtime.Serialization.Json

[DataContract]
public class Foo

   [DataMember(Name = "data")]
   public Dictionary<string,string> Data  get; set; 

然后像这样使用它:

var serializer = new DataContractJsonSerializer(typeof(List<Foo>));
var jsonParams = @"""data"": [""Key"":""foo"",""Value"":""bar""] ";
var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonParams));

var obj = serializer.ReadObject(stream);
Console.WriteLine(obj);

【讨论】:

序列化程序在哪里定义? ..什么是 Category3MeasureModel?在 Google 上没有点击。 这只是我为我的项目序列化的模型类。它应该是那个 Foo 类,但我从生产代码中重新复制了整个部分。你应该创建自己的,比如我的 Foo 类。我将其重命名为 Foo 以使其更简单。它只是您希望序列化为 json 并返回的属性或字段的类。 @DanCsharpster 在 Windows Phone 8.1 Silverlight 上,我得到了您的代码的精确复制:`System.ServiceModel.Web.ni 中发生了“System.Security.SecurityException”类型的异常.dll 但未在用户代码中处理附加信息:数据协定类型“MyApp.Foo”无法反序列化,因为成员“数据”不是公共的。将成员设为公开将修复此错误。或者,您可以将其设为内部,并在程序集上使用 InternalsVisibleToAttribute 属性以启用内部成员的序列化 @DanCsharpster 当将属性 Data 更改为成员时(没有 get;set;),我得到: System.ServiceModel.Web 中发生了“System.ArgumentException”类型的第一次机会异常。 ni.dll 附加信息:“System.Object”类型的对象无法转换为“System.Collections.Generic.Dictionary`2[System.String,System.String]”类型。【参考方案22】:

这是我使用 System.Text.Json 的解决方案。你会得到一个嵌套对象的 json 字符串,稍后可以将其转换为所需的类型。

public static Dictionary<string,string> JsonToDictionary(this string json)
        
            var objectValues = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
            var stringValues = objectValues.Select(o => new KeyValuePair<string, string>(o.Key, o.Value?.ToString()));
            return stringValues.ToDictionary(pair => pair.Key, pair => pair.Value);
        

这是从嵌套对象中获取值的用法示例:

 var result= json.JsonToDictionary()["outerField"]
                .JsonToDictionary()["innerField"];

请注意,此解决方案不包括以数组开头的 json 对象,如 [12, 13]。这些对象可以在开始时作为数组读取,然后可以对每个项目应用扩展方法,以防项目是具有自己属性的复杂对象。

【讨论】:

以上是关于如何在 ASP.NET 中将 JSON 反序列化为简单的 Dictionary<string,string>?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用客户端的时区将 asp.net 日期时间反序列化为 JSON 日期时间

在将 json 反序列化为对象时,使用 jackson 将 asp.net / MS 专有 json Dateformat 转换为 java8 LocalDateTime

ASP.Net Core 中的 JSON 序列化/反序列化

如何正确地将 asp.net 核心模型状态错误反序列化为 xamarin 形式的对象

在 C# 中将 JSON 数组反序列化为对象

在 Android Studio 中将嵌套的 JSON 反序列化为 Java