How can I deserialize JSON to a simple Dictionary<string,string> in ASP.NET?
Asked Answered
S

22

860

I have a simple key/value list in JSON being sent back to ASP.NET via POST. Example:

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

I AM NOT TRYING TO DESERIALIZE INTO STRONGLY-TYPED .NET OBJECTS

I simply need a plain old Dictionary(Of String, String), or some equivalent (hash table, Dictionary(Of String, Object), old-school StringDictionary--hell, a 2-D array of strings would work for me.

I can use anything available in ASP.NET 3.5, as well as the popular Json.NET (which I'm already using for serialization to the client).

Apparently neither of these JSON libraries have this forehead-slapping obvious capability out of the box--they are totally focused on reflection-based deserialization via strong contracts.

Any ideas?

Limitations:

  1. I don't want to implement my own JSON parser
  2. Can't use ASP.NET 4.0 yet
  3. Would prefer to stay away from the older, deprecated ASP.NET class for JSON
Shelleyshellfire answered 30/7, 2009 at 16:28 Comment(3)
re: limitation 3, JavaScriptSerizlizer is used in ASP.NET MVC and is no longer deprecated.Acarology
it's incredible how difficult it was to find a simple way to convert a json string into something I could easily use without flipping through many different stackoverflow. It's so easy in other languages yet Java and C# seems to go out of their way to make life difficult.Ciborium
Well, specifically System.Text.Json seems to be on a mission to make JSON as hard as it can, and regularly fail to observe the whole point of JSON, which is to be greedy. These things tend to "just work" with Newtonsoft/Json.Net though.Archibaldo
W
1116

Json.NET does this...

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

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

More examples: Serializing Collections with Json.NET

Wolfgang answered 31/7, 2009 at 11:51 Comment(11)
Does this also work when youre values are integers. Are they automatically casted to 'strings'?Coucal
@Coucal No it does not. I have found the best way to deserialize into a dictionary is to use dynamic as the type for the values: JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);Delibes
Tried several answers on this page with a very messy key/value pair, and JSON.NET was the only one that I tried that worked.Thymelaeaceous
Doesn't work if you're using an array of key value pairs in json [{key: "a", value: "1"}, {key: "b", value:"2"}] you have to do something like this: var dict = JsonConvert.DeserializeObject<List<KeyValuePair<string, string>>>(json);Paff
Also does not work if the values are nested objects, because json.net creates those as JObjectsMccusker
When the source is instead from LINQ, how do we do this? I cannot believe this is so difficult. I can't use DeseralizeObject on a System.Linq.IqueryableWotton
For anyone wondering, here's the solution I found: ToDictionary #954419. I'm using this to populate a jqGrid dropdown from LINQWotton
I second @ErikSchierboom 's answer. Using dynamic type will also work for lists and json "objects". If your json is complex and nested <string,string> won't workSmoothen
This was the page in the docs that I needed: newtonsoft.com/json/help/html/DeserializeDictionary.htmDempstor
Note that dynamic type can not be used on iOS wintellect.com/cant-use-dynamic-c-keyword-xamarin-iosInextricable
<string, object> results in a JsonElement. Not nearly as useful as NewtonDike
B
115

I did discover .NET has a built in way to cast the JSON string into a Dictionary<String, Object> via the System.Web.Script.Serialization.JavaScriptSerializer type in the 3.5 System.Web.Extensions assembly. Use the method DeserializeObject(String).

I stumbled upon this when doing an ajax post (via jquery) of content type 'application/json' to a static .net Page Method and saw that the method (which had a single parameter of type Object) magically received this Dictionary.

Bertberta answered 29/1, 2010 at 1:54 Comment(4)
but the built in javascriptserializer is buggier than json.net, that solution is better. For example the javascriptseralizer will return nulls instead of blank strings, and doesn't work at all for nullable properties, and so on.Unreadable
@Unreadable Not to mention the fun you have when trying to Parse dates as it assumes MS's non-standard date format.Unreflecting
Quick code example: var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); followed by Dictionary<string, object> dict = (Dictionary<string, object>)jsSerializer.DeserializeObject(jsonString);Lungki
Advantage of Nate Cook's example in a simple case is to avoid the need for external DLLs. I'm accessing an API from a standalone console that can only rely the .Net framework.Asyut
T
94

System.Text.Json

This can now be done using System.Text.Json which is built-in to .NET Core 3.0. It's now possible to deserialize JSON without using third-party libraries.

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

Also available in NuGet package System.Text.Json if using .NET Standard or .NET Framework.


Make sure to read and understand:

Topsail answered 23/10, 2019 at 11:9 Comment(7)
Yes! System.Text.Json is the way to go these days.Theorist
Yes, it looks promising! However, note that .NET Core 3.1's default version of System.Text.Json does not support deserializing Dictionaries with non-string keys. While my OP was about strings, in practice now, I do have a lot of Guid keys, so this "bit" me when trying to make the switch. It also doesn't have equivalents of some of the attributes (required, etc.).Shelleyshellfire
Nice answer, but the source of .NET json functions is Newtonsoft.Json;Haymo
If you have nested data I recommend Dictionary<string, JsonElement> that way you can test the object value with childNode.Value.ValueKind == JsonValueKind.Object and deserialize again using JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(childNode.Value);Gilroy
Note that .NET Core 3.0 in which System.Text.Json.JsonSerializer was introduced came in 2019, while some of the other answers in this thread are older.Exclusion
I don't recommend it. If type is somehow different from string type it will throw an exception. I got this error for example: Error converting value "49b9dbb82a21b9001f803bbeb45ad23798464ec2c47f073f6437c933fa4022b0" to type "System.Text.Json.JsonElement". Could not cast or convert from System.String to System.Text.Json.JsonElement.Incommode
@ZiCold: Maybe if you used <string, object> instead.Nombles
C
58

For those searching the internet and stumbling upon this post, I wrote a blog post on how to use the JavaScriptSerializer class.

Read more... http://procbits.com/2011/04/21/quick-json-serializationdeserialization-in-c/

Here is an example:

var json = "{\"id\":\"13\", \"value\": true}";
var jss = new JavaScriptSerializer();
var table = jss.Deserialize<dynamic>(json);
Console.WriteLine(table["id"]);
Console.WriteLine(table["value"]);
Crispate answered 21/4, 2011 at 18:41 Comment(4)
hm, I've tried your solution...I have json like this {"id":"13", "value": true} and for me only Dictionary<dynamic> solution worksLavonna
ok I found it where is the problem...you need to add [] after dictionary declaration in order to deserialize properly...I'm adding comment to your blog post too... cheers ;)Lavonna
I've updated my answer to reflect your specific dataset. It works fine with dynamic.Crispate
I just wrote another JSON parser that is a bit more flexible and supports Silverlight: procbits.com/2011/08/11/…Crispate
O
48

I had the same problem, so I wrote this my self. This solution is differentiated from other answers because it can deserialize in to multiple levels.

Just send JSON string in to deserializeToDictionary function it will return non strongly-typed Dictionary<string, object> object.

Old code

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

Ex: This will return Dictionary<string, object> object of a Facebook JSON response.

Test

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

Note: hometown further deserilize into a Dictionary<string, object> object.

Update

My old answer works great if there is no array on JSON string. This one further deserialize in to a List<object> if an element is an array.

Just send a JSON string in to deserializeToDictionaryOrList function it will return non strongly-typed Dictionary<string, object> object or 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;
    }
}
Odeliaodelinda answered 25/7, 2011 at 11:19 Comment(4)
@Jordan thanks for pointing out, I made some modification to this code but I don't have it now. This code doesn't handle JArray objects, I'll update the code once I have it.Odeliaodelinda
Not a problem. I only mention it because learning about the is and as operators greatly helped me and simplified my own code.Adverse
It works, but not efficient, because it calls ToString and then Deserialize again. Look at Falko's answer below. It is deserialize the source string only once.Fuld
Falko's answer works only if you know the data structure in advance. This solution can use for any JSON string.Odeliaodelinda
G
42

Tried to not use any external JSON implementation so i deserialised like this:

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);
Grillparzer answered 12/1, 2012 at 10:51 Comment(2)
Add reference System.Web.Extensions to use System.Web.ScriptBoneset
I like this answer best because it is simple and uses the .NET System.Web.Script.Serialization. It just works. I was even able to use "invalid" JSON like string json = "{'id':13, 'value': true}";.Atmometer
T
20

I just needed to parse a nested dictionary, like

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

where JsonConvert.DeserializeObject doesn't help. I found the following approach:

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

The SelectToken lets you dig down to the desired field. You can even specify a path like "x.y.z" to step further down into the JSON object.

Thrall answered 11/8, 2016 at 18:46 Comment(1)
JObject.Parse(json).ToObject<Dictionary<Guid, List<int>>>() worked for me in my scenario thanksCrescin
G
19

If you're after a lightweight, no-added-references kind of approach, maybe this bit of code I just wrote will work (I can't 100% guarantee robustness though).

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

[I realise that this violates the OP Limitation #1, but technically, you didn't write it, I did]

Groat answered 2/11, 2012 at 9:26 Comment(7)
That's the only answer working for Silverlight and without dependency! Silverlight doesn't have JavascriptSerializer or Serializable. And no dependency means no Json.NET, RestSharp or MiniJSON. Only @DanCsharpster tried another possible solution but unfortunately it wasn't working for me like this one does.Gerita
What is wrong with adding a reference to something simple like JSON.NET? Does it need to be that lightweight that you can't reference anything? I'm not saying your code won't work, but anytime you roll your own, you obviously run the risk of your code not being as robust, for things like edge cases, or fast as a tested library like JSON.NET.Gustav
Rolling your own is a bad idea when you have a good alternative. I know of no situation that has to be that lightweight. And I would rather have less optimal code that is easy to read and change.Adverse
I originally wrote that piece of code because I had no alternative. Consider things like Silverlight, or providers of various types for Office products, where adding external references to the project is either extremely problematic or impossible.Groat
I know its a few years later, but this is still a very valid question. To anyone wondering why would we want to go so lightweight, well, if you are working with SQL CLR C#, there's only so many "safe" libraries that you are able to use and System.RunTime.Serialization is not one of them, unfortunately JSON.NET depends on it and thus it cannot be used either. Thanks dexy for your excelent work, I dared to improve on it a bit to be able to deserialize arrays of arrays, see the updated code in my answer here.Uniocular
Great Implementation, it properly converted nested objects for meLilas
This is great, but with the JSON that I'm using it prepends a new line in every key of the dictionary. To solve it just append a ".TrimStart()" at the end of the RegEx line in DecodeString function.Decker
M
7

For anyone who is trying to convert JSON to dictionary just for retrieving some value out of it. There is a simple way using 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
Mecham answered 26/11, 2019 at 5:55 Comment(0)
S
6

Edit: This works, but the accepted answer using Json.NET is much more straightforward. Leaving this one in case someone needs BCL-only code.

It’s not supported by the .NET framework out of the box. A glaring oversight – not everyone needs to deserialize into objects with named properties. So I ended up rolling my own:

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

same on 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]);
    }
}

Called with:

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"]);

Sorry for the mix of C# and VB.NET…

Shelleyshellfire answered 30/7, 2009 at 18:59 Comment(6)
[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"]); }Pungent
This is fantastic. Helps with WCF service implementations that interface using JSON with browser-based clients.Drowsy
@Mark Rendle: Your implementation is sooo simple, and is the ONLY one that has worked for me so far in getting both the success and error-codes json results. I've tried many solutions, so thank you for posting that as a comment. It should be the answer.Bergschrund
Not work (at least at .NetFramework 4.6.1): //Because of num of char. at comment limit I'll post some 1) as posted (class just inherits ISerializable) - got exception: "InvalidDataContractException: Type 'StringStringDictionary' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required."Hershelhershell
2) as class & its dict field are already public, try to add [DataContract] & [DataMember] as sugested - Got exception: "System.Runtime.Serialization.InvalidDataContractException: ISerializable type 'Translator.Utils.JsonReader+StringStringDictionary' cannot have DataContract." 3) Ok, try just [DataContract] & [DataMember] attributes (w/o ISerializable inheritance) - ReadObject now finished w/o exceptions, but it didn't parsed actually: myfields.dict == nullHershelhershell
4) as previous (3), but rewrite json text a little: MyJsonString = "{ \"dict\" : { \"key1\": \"value1\", \"key2\": \"value2\"} }" - myfields.dict != null but its .Count==0, so it also doesn't actually parse. 5) I also tried ISerializable inheritance + mark class by [Serializable] attribute - Got exception: "System.Reflection.TargetParameterCountException: Number of parameters specified does not match the expected number."Hershelhershell
B
6

Mark Rendle posted this as a comment, I wanted to post it as an answer since it's the only solution that has worked so far to return the success and the error-codes json results from the Google reCaptcha response.

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

Thanks again, Mark!

Bergschrund answered 20/3, 2015 at 0:6 Comment(2)
The JavaScriptSerializer has been almost deprecated. The documentation says we should be using JSON.NET (learn.microsoft.com/en-us/dotnet/api/…)Gaucho
Also good for legacy webforms apps where you don't want to include additional dependencies.Thrown
A
5

I've added upon the code submitted by jSnake04 and Dasun herein. I've added code to create lists of objects from JArray instances. It has two-way recursion but as it is functioning on a fixed, finite tree model, there is no risk of stack overflow unless the data is massive.

/// <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;
}
Adverse answered 3/3, 2015 at 18:56 Comment(0)
L
4

I added a check for null values in the JSON to the other answer

I had same problem so I wrote this my self. This solution is differentiated from other answers because it can deserialize in to multiple levels.

Just send json string in to deserializeToDictionary function it will return non strongly-typed Dictionary<string, object> 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;
}

Ex: This will return Dictionary<string, object> object of a Facebook JSON response.

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

Note: hometown further deserialize into a Dictionary<string, object> object.

Leckie answered 6/5, 2014 at 23:42 Comment(1)
+1 As I said with Dasun above. You could just check whether d.Value is JObject. You don't have to go through reflection to check types. And with the is operator you don't need to check for null. It returns false if the object is null.Adverse
M
3

My approach directly deserializes to IDictionary, without JObject or ExpandObject in between. The code uses converter, which is basically copied from ExpandoObjectConverter class found in JSON.NET sourcecode, but using IDictionary instead of ExpandoObject.

Usage:

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

Code:

// 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;
    }
}
Makell answered 26/5, 2014 at 9:53 Comment(0)
B
3

It seems all of these answers here just assume you can get that little string out of a bigger object... for people looking to simply deserealize a large object with such a dictionary somewhere inside the mapping, and who are using the System.Runtime.Serialization.Json DataContract system, here's a solution:

An answer on gis.stackexchange.com had this interesting link. I had to recover it with archive.org, but it offers a pretty much perfect solution: a custom IDataContractSurrogate class in which you implement exactly your own types. I was able to expand it easily.

I made a bunch of changes in it, though. Since the original source is no longer available, I'll post the entire class here:

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

To add new supported types to the class, you just need to add your class, give it the right constructors and functions (look at SurrogateDictionary for an example), make sure it inherits JsonSurrogateObject, and add its type mapping to the KnownTypes dictionary. The included SurrogateDictionary can serve as basis for any Dictionary<String,T> types where T is any type that does deserialize correctly.

Calling it is really simple:

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

Note that for some reason this thing has trouble using key strings which contain spaces; they were simply not present in the final list. Might just be it's simply against json specs and the api I was calling was poorly implemented, mind you; I dunno. Anyway, I solved this by regex-replacing them with underscores in the raw json data and fixing the dictionary after the deserialization.

Bywoods answered 6/8, 2015 at 19:51 Comment(3)
By the way, for some peculiar reason Mono seems to have trouble running this stuff...Bywoods
Thanks for sharing, unfortunately this solution doesn't support non-primitive types, and there's no way to get the raw value, so you can construct it yourself. If I register my custom type in KnownTypes and use it in dictionary, it calls dictionary first, I would expect it to start parsing bottom to top from the most remote types, to the more complex ones.Vachill
Well, the question only asked about Dictionary<String,String>. I honestly never tried deserializing complex types with this system.Bywoods
S
3

Based on comments above try 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);

seems to work even for complex objects and lists.

Smoothen answered 19/12, 2017 at 22:55 Comment(0)
B
3

Here is my solution with System.Text.Json. You get a json string for the nested objects which in own turn can be converted to needed type later on.

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

Here is the usage example to fetch values from a nested object:

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

Note that this solution does not cover the json objects starting as an array like [12, 13]. These objects can be read as an array in the begining and then the extension method can be applied on each item, in case the items are complex objects with their own properties.

Bier answered 14/12, 2021 at 16:11 Comment(1)
error : cannot convert from string to Newtonsoft.Json.JsonReader.Phillida
T
2

A bit late to the game, but non of the above solutions pointed me in the direction of a pure and simple .NET, no json.net solution. So here it is, ended up being very simple. Below a full running example of how it is done with standard .NET Json serialization, the example has dictionary both in the root object and in the child objects.

The golden bullet is this cat, parse the settings as second parameter to the serializer:

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

Full code below:

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);
                        }
                    }
                }
            }
        }
    }
Tootsie answered 13/12, 2017 at 15:45 Comment(0)
D
1

Annoyingly enough, if you want to use the default model binders, it looks like you will have to use numerical index values like a form POST.

See the following excerpt from this article http://msdn.microsoft.com/en-us/magazine/hh781022.aspx:

Though it’s somewhat counterintuitive, JSON requests have the same requirements—they, too, must adhere to the form post naming syntax. Take, for example, the JSON payload for the previous UnitPrice collection. The pure JSON array syntax for this data would be represented as:

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

However, the default value providers and model binders require the data to be represented as a JSON form post:

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

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

The complex object collection scenario is perhaps one of the most widely problematic scenarios that developers run into because the syntax isn’t necessarily evident to all developers. However, once you learn the relatively simple syntax for posting complex collections, these scenarios become much easier to deal with.

Dimmick answered 30/5, 2012 at 14:59 Comment(0)
D
1

I just implemented this in RestSharp. This post was helpful to me.

Besides the code in the link, here is my code. I now get a Dictionary of results when I do something like this:

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

Be mindful of the sort of JSON you're expecting - in my case, I was retrieving a single object with several properties. In the attached link, the author was retrieving a list.

Dismay answered 2/5, 2013 at 18:59 Comment(0)
G
0

I would suggest using System.Runtime.Serialization.Json that is part of .NET 4.5.

[DataContract]
public class Foo
{
   [DataMember(Name = "data")]
   public Dictionary<string,string> Data { get; set; }
}

Then use it like this:

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);
Gustav answered 9/6, 2013 at 21:29 Comment(6)
Where is serializer defined?Thymelaeaceous
..and what is a Category3MeasureModel? No hits on Google.Thymelaeaceous
That's just the model class that I'm serializing for my project. It's supposed to be that Foo class, but I recopied the whole section from production code. You should create your own, like my Foo class. I renamed it to Foo to make it simpler. It's just a class of the properties or fields that you want serialized out to json and back.Gustav
@DanCsharpster With an exact copy-past of your code, I get, on Windows Phone 8.1 Silverlight: `An exception of type 'System.Security.SecurityException' occurred in System.ServiceModel.Web.ni.dll but was not handled in user code Additional information: The data contract type 'MyApp.Foo' cannot be deserialized because the member 'Data' is not public. Making the member public will fix this error. Alternatively, you can make it internal, and use the InternalsVisibleToAttribute attribute on your assembly in order to enable serialization of internal membersGerita
@DanCsharpster And when changing the property Data to be a member (without get; set;), I get: A first chance exception of type 'System.ArgumentException' occurred in System.ServiceModel.Web.ni.dll Additional information: Object of type 'System.Object' cannot be converted to type 'System.Collections.Generic.Dictionary`2[System.String,System.String]'.Gerita
This seems to be an issue with the built in serializer in this situation (windows phone 8). A quick glance on the web seems to support what you found. Their recommendation was to use JSON.NET. groups.google.com/forum/#!topic/reactivexaml/Rrq4xLoVe38Gustav
C
0

You could use Tiny-JSON

string json = "{\"key1\":\"value1\", \"key2\":\"value2\"}";
IDictionary<string, string> dict = Tiny.Json.Decode<Dictionary<string, string>>(json);
Classmate answered 21/9, 2016 at 11:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.