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


679

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

{ "key1": "value1", "key2": "value2"}

我不打算将序列化为严格类型的.NET对象

我只需要一个普通的旧Dictionary(Of String,String)或一些等效项(哈希表,Dictionary(Of String,Object),老式的StringDictionary-地狱,二维字符串数组对我来说就可以工作。

我可以使用ASP.NET 3.5和流行的Json.NET(我已经在使用它序列化客户端)中使用的任何东西。

显然,这两个JSON库都不具有这种开箱即用的明显功能-它们完全专注于通过强契约进行基于反射的反序列化。

有任何想法吗?

局限性:

  1. 我不想实现自己的JSON解析器
  2. 尚不能使用ASP.NET 4.0
  3. 希望远离JSON不再使用的旧的ASP.NET类

1
关于:限制3,JavaScriptSerizlizer在ASP.NET MVC中使用,并且不再弃用。
bdukes,2011年

16
难以置信的是,找到一种简单的方法来将json字符串转换为我可以轻松使用而又无需经历许多不同的stackoverflow的事情,这是多么困难。在其他语言中这是如此容易,但是Java和C#似乎竭尽全力使生活变得困难。
user299709'6

Answers:


891

Json.NET做到了...

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

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

更多示例:使用Json.NET序列化集合


9
当您的值是整数时,这是否也起作用?它们会自动转换为“字符串”吗?
Highmastdon 2012年

58
@Highmastdon不,不是。我发现反序列化为字典的最佳方法是将其dynamic用作值的类型:JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
Erik Schierboom 2013年

1
使用非常混乱的键/值对在此页面上尝试了几个答案,而JSON.NET是我尝试的唯一可行的方法。
bnieland 2014年

15
如果您在json中使用键值对数组不起作用,则[{key: "a", value: "1"}, {key: "b", value:"2"}]必须执行以下操作:var dict = JsonConvert.DeserializeObject<List<KeyValuePair<string, string>>>(json);
Adrian

8
如果值是嵌套对象,则也不起作用,因为json.net将其创建为JObjects
Kugel

100

我确实发现.NET具有一种内置方式,可以Dictionary<String, Object>通过System.Web.Script.Serialization.JavaScriptSerializer3.5 System.Web.Extensions程序集中的类型将JSON字符串转换为via 。使用方法DeserializeObject(String)

当我将内容类型为'application / json'的ajax发布(通过jquery)发布到静态.net Page Method时,我偶然发现了该方法(具有单个type的参数Object)神奇地接收了此Dictionary。


5
但是内置的javascriptserializer比json.net的buggier多,该解决方案更好。例如,javascriptseralizer将返回null而不是空白字符串,并且对于可为null的属性完全不起作用,依此类推。
pilavdzice 2012年

1
@pilavdzice更不用说您尝试解析日期时的乐趣了,因为它假定了MS的非标准日期格式。
2012年

16
快速代码示例:var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();随后Dictionary<string, object> dict = (Dictionary<string, object>)jsSerializer.DeserializeObject(jsonString);
Nate Cook

6
在简单情况下,Nate Cook的示例的优势在于避免了对外部DLL的需求。我正在从只能依赖.Net框架的独立控制台访问API。
Nick.T

@pilavdzice您可以详细说明吗?我无法重现“返回null而不是空白字符串”的情况,它为我提供了一个空白字符串值SomeData: ""
jrh

51

对于那些在互联网上搜寻并绊脚石的人,我写了一篇有关如何使用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"]);

嗯,我已经尝试了您的解决方案...我有一个像{{id“:” 13“,” value“:true}这样的json,对我来说,只有Dictionary <dynamic>解决方案有效
Marko

好的,我发现问题出在哪里...您需要在字典声明后添加[]以正确反序列化...我也要在您的博客文章中添加评论...,欢呼声;)
Marko

我已经更新了答案,以反映您的特定数据集。动态效果很好。
JP理查森,

我刚刚写了另一个更灵活并支持Silverlight的JSON解析器:procbits.com/2011/08/11/…–
JP理查森

41

试图不使用任何外部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);

6
添加参考System.Web.Extensions以使用System.Web.Script
Patrick Cullen 2012年

1
我最喜欢这个答案,因为它很简单并且使用.NET System.Web.Script.Serialization。它只是工作。我什至可以使用像这样的“无效” JSON string json = "{'id':13, 'value': true}";
styfle 2012年

出于好奇,是否有相同的一种方法将反序列化为OrdinalIgnoreCase词典?
batbaatar 2014年

38

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

只需将JSON字符串发送到deserializeToDictionary函数中,它将返回非强类型Dictionary<string, object>对象。

旧代码

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;
}

例如:这将返回Dictionary<string, object>Facebook 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);
}

注意:家乡进一步反序列化为Dictionary<string, object> 对象。

更新资料

如果JSON字符串上没有数组,我的旧答案将非常有用。List<object>如果元素是数组,则此方法进一步反序列化为a 。

只需将JSON字符串发送到deserializeToDictionaryOrList函数中,它将返回非强类型Dictionary<string, object>对象或List<object>

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对象,一旦有了它,我将对其进行更新。
Dasun

1
没问题 我之所以只提到它,是因为学习isand as运算符对我有很大帮助,并简化了我自己的代码。
约旦

它有效,但效率不高,因为它先调用ToString,然后再次进行反序列化。请在下面查看Falko的答案。仅对源字符串反序列化一次。
谢尔盖·兹诺维耶夫

1
只有事先知道数据结构,Falko的答案才有效。此解决方案可以用于任何JSON字符串。

16

如果您追求一种轻量级的,无添加引用的方法,那么也许我刚刚编写的这段代码会起作用(不过,我不能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,但从技术上讲,您没有编写它,我确实这样做了]


3
那是在Silverlight上没有依赖的唯一答案!Silverlight没有JavascriptSerializer或Serializable。没有依赖意味着没有Json.NET,RestSharp或MiniJSON。只有@DanCsharpster尝试了另一种可能的解决方案,但不幸的是,它对我不起作用。
2014年

1
添加对诸如JSON.NET之类的简单内容的引用有什么问题?是否需要轻量级而您不能引用任何内容?我并不是说您的代码行不通,但是无论您何时滚动自己的代码,显然都冒着代码不那么健壮的风险,比如边缘情况,或者像JSON.NET这样的经过测试的库一样快。
Dan Csharpster 2014年

1
当您有一个不错的选择时,自己动手是个坏主意。我知道没有任何情况必须如此轻巧。而且,我宁愿选择不太容易阅读和更改的最佳代码。
约旦

3
我最初写那段代码是因为我别无选择。考虑一下诸如Silverlight或Office产品的各种类型的提供程序之类的事情,在该项目中向项目添加外部引用是非常有问题或不可能的。
dexy 2015年

几年后我知道了,但这仍然是一个非常有效的问题。对于任何想知道为什么我们要如此轻量级的人来说,好吧,如果您正在使用SQL CLR C#,那么只能使用那么多“安全”库,System.RunTime.Serialization而其中却不是其中之一,不幸的是JSON.NET依赖于它,因此也无法使用。感谢dexy所做的出色工作,我敢于对此进行一些改进,以便能够对数组数组进行反序列化,请参见此处的最新代码
艾伯托·雷奇

15

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

{
    "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 <Dictionary <Guid,List <int >>>()在我的场景中为我工作了
geedubb,2016年

11

系统文本

现在可以使用System.Text.Json内置的来完成此操作.net core 3.0。现在可以在使用第三方库的情况下反序列化JSON 。

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

如果使用.Net Standard或.Net Framework,也可以在nu-get包System.Text.Json中获得。


1
是! System.Text.Json这几天是要走的路。
mfluehr

2
是的,看起来很有希望!但是,请注意,.NET Core 3.1的默认版本的System.Text.Json不支持使用非字符串键反序列化Dictionary。虽然我的OP是关于字符串的,但是现在实际上,我确实有很多Guid键,因此在尝试进行切换时会“有点”麻烦。它也没有某些属性的等效项(必填等)。
richardtallent19年

6

马克·雷德尔(Mark Rendle)将此作为评论发表,我想将其作为答案发表,因为它是迄今为止返回成功的唯一解决方案,并且是来自Google reCaptcha响应的错误代码json结果。

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

再次感谢,马克!


1
JavaScriptSerializer几乎已弃用。文档说我们应该使用JSON.NET(docs.microsoft.com/zh-cn/dotnet/api/…
Mario Lopez

对于不想包含其他依赖项的旧版Webforms应用程序也很有效。
安德鲁·格罗斯

5

编辑:这可行,但是使用Json.NET的可接受答案要简单得多。如果有人需要仅BCL的代码,则不要使用此代码。

现成的.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

致电:

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的组合…


2
[TestMethod] public void TestSimpleObject(){const string json = @“ {”“ Name”“:”“ Bob”“,”“ Age”“:42}”; var dict = new JavaScriptSerializer()。DeserializeObject(json)as IDictionary <string,object>; Assert.IsNotNull(dict); Assert.IsTrue(dict.ContainsKey(“ Name”))); Assert.AreEqual(“ Bob”,dict [“ Name”]); Assert.IsTrue(dict.ContainsKey(“ Age”)); Assert.AreEqual(42,dict [“ Age”]); }
Mark Rendle

1
这是太棒了。帮助WCF服务实现与基于浏览器的客户端结合使用JSON。
安东

@Mark Rendle:您的实现非常简单,并且到目前为止,这对我来说一直都是成功的,同时获得了成功和错误代码json结果。我已经尝试了许多解决方案,因此感谢您将其发布为评论。应该是答案。
Bryan

5

我在这里添加了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;
}

4

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

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

只需将json字符串发送到deserializeToDictionary函数中,它将返回非强类型Dictionary<string, object>对象。

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;
}

例如:这将返回Dictionary<string, object>Facebook 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);
}

注意:家乡进一步反序列化为Dictionary<string, object>对象。


1
+1就像我在上面提到的Dasun一样。您可以检查是否d.Value is JObject。您不必经过反思即可检查类型。使用is运算符,您无需检查null。如果对象为null,则返回false。
约旦

3

似乎所有这些答案都只是假设您可以从较大的对象中提取出很小的字符串...对于希望通过映射内部某处的此类字典简单地反分解大型对象的人,以及正在使用System.Runtime.Serialization.JsonDataContract系统的人们,这里一个解法:

gis.stackexchange.com上的答案具有此有趣的链接。我不得不通过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<String,T>类型的基础,其中T是可以正确反序列化的任何类型。

调用非常简单:

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

注意,由于某种原因,这东西在使用包含空格的键字符串时会遇到麻烦;它们根本不在最终列表中。可能只是因为它只是针对json规范,而我正在调用的api实施得不好,请注意;我不知道。无论如何,我通过在原始json数据中用下划线替换它们并在反序列化后修复字典来解决此问题。


顺便说一句,由于某些特殊原因,Mono似乎在运行这些东西时遇到了麻烦……
Nyerguds

感谢您的共享,很遗憾,此解决方案不支持非原始类型,并且无法获取原始值,因此您可以自己构造它。如果我在KnownTypes中注册我的自定义类型并在字典中使用它,它将首先调用字典,我希望它会开始从最远端的类型到更复杂的类型进行从下到上的解析。
伊万·列昂

好吧,这个问题只问了一下Dictionary<String,String>。老实说,我从未尝试使用此系统反序列化复杂类型。
Nyerguds

3

根据以上评论尝试JsonConvert.DeserializeObject<Dictionary<string,dynamic>>(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);

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


1

我只是在RestSharp中实现了这一点这篇文章对我很有帮助。

除了链接中的代码,这是我的代码。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类型-在我的情况下,我正在检索具有多个属性的单个对象。在附件的链接中,作者正在检索列表。


1

我的方法直接反序列化为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;
    }
}

1

您可以使用Tiny-JSON

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

1

游戏有点晚了,但是以上解决方案中没有一个使我朝着纯粹而简单的.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);
                        }
                    }
                }
            }
        }
    }

0

十分烦人的是,如果您要使用默认的模型绑定程序,则看起来您将不得不使用数字索引值(如POST表单)。

请参阅本文的以下摘录http://msdn.microsoft.com/zh-cn/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
}

复杂的对象集合方案可能是开发人员遇到的最广泛问题的方案之一,因为语法不一定对所有开发人员都显而易见。但是,一旦您了解了用于发布复杂集合的相对简单的语法,这些情况就会变得更加容易处理。


0

我建议使用System.Runtime.Serialization.Json它是.NET 4.5的一部分。

[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);

序列化器在哪里定义?
bnieland 2014年

..什么是Category3MeasureModel?在Google上没有任何点击。
bnieland 2014年

1
那只是我为项目序列化的模型类。应该是Foo类,但是我从生产代码中复制了整个部分。您应该创建自己的类,例如我的Foo类。我将其重命名为Foo以使其更简单。它只是您要序列化为json并返回的属性或字段的一类。
Dan Csharpster 2014年

1
@DanCsharpster在Windows Phone 8.1 Silverlight上,我得到了与您的代码完全一样的过去的复制:System.ServiceModel.Web.ni.dll中发生了'System.Security.SecurityException类型的异常,但未在用户中处理代码附加信息:数据合同类型'MyApp.Foo'无法反序列化,因为成员'Data'不是公共的。公开该成员将解决此错误。或者,您可以将其设置为内部,并在程序集中使用InternalsVisibleToAttribute属性以启用内部成员的序列化
2014年

1
@DanCsharpster并且,当将属性Data更改为成员(没有get; set;)时,我得到:System.ServiceModel.Web.ni.dll中发生了类型'System.ArgumentException'的第一次机会异常附加信息:对象类型“ System.Object”不能转换为类型“ System.Collections.Generic.Dictionary`2 [System.String,System.String]”。
2014年

0

对于任何试图将JSON转换为字典的人,只是为了从中获取一些价值。有一个简单的使用方法Newtongsoft.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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.