Monday, 1 May 2017

.net - Deserialize JSON into C# dynamic object?



Is there a way to deserialize JSON content into a C# 4 dynamic type? It would be nice to skip creating a bunch of classes in order to use the DataContractJsonSerializer.


Answer



If you are happy to have a dependency upon the System.Web.Helpers assembly, then you can use the Json class:



dynamic data = Json.Decode(json);



It is included with the MVC framework as an additional download to the .NET 4 framework. Be sure to give Vlad an upvote if that's helpful! However if you cannot assume the client environment includes this DLL, then read on.






An alternative deserialisation approach is suggested here. I modified the code slightly to fix a bug and suit my coding style. All you need is this code and a reference to System.Web.Extensions from your project:



using System;
using System.Collections;

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

public sealed class DynamicJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer)

{
if (dictionary == null)
throw new ArgumentNullException("dictionary");

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

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

}

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

#region Nested type: DynamicJsonObject

private sealed class DynamicJsonObject : DynamicObject

{
private readonly IDictionary _dictionary;

public DynamicJsonObject(IDictionary dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_dictionary = dictionary;
}


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

private void ToString(StringBuilder sb)
{
var firstInDictionary = true;

foreach (var pair in _dictionary)
{
if (!firstInDictionary)
sb.Append(",");
firstInDictionary = false;
var value = pair.Value;
var name = pair.Key;
if (value is string)
{
sb.AppendFormat("{0}:\"{1}\"", name, value);

}
else if (value is IDictionary)
{
new DynamicJsonObject((IDictionary)value).ToString(sb);
}
else if (value is ArrayList)
{
sb.Append(name + ":[");
var firstInArray = true;
foreach (var arrayValue in (ArrayList)value)

{
if (!firstInArray)
sb.Append(",");
firstInArray = false;
if (arrayValue is IDictionary)
new DynamicJsonObject((IDictionary)arrayValue).ToString(sb);
else if (arrayValue is string)
sb.AppendFormat("\"{0}\"", arrayValue);
else
sb.AppendFormat("{0}", arrayValue);


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

}

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


result = WrapResultObject(result);
return true;
}

public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1 && indexes[0] != null)
{
if (!_dictionary.TryGetValue(indexes[0].ToString(), out result))

{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}

result = WrapResultObject(result);
return true;
}


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

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

var arrayList = result as ArrayList;

if (arrayList != null && arrayList.Count > 0)
{
return arrayList[0] is IDictionary
? new List(arrayList.Cast>().Select(x => new DynamicJsonObject(x)))
: new List(arrayList.Cast());
}

return result;
}
}


#endregion
}


You can use it like this:



string json = ...;

var serializer = new JavaScriptSerializer();

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

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


So, given a JSON string:



{
"Items":[
{ "Name":"Apple", "Price":12.3 },

{ "Name":"Grape", "Price":3.21 }
],
"Date":"21/11/2010"
}


The following code will work at runtime:



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


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

No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...