2017  Kodetalk | Feedback | Privacy Policy | Terms | About
userimage

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.
userimage

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<string, object> dictionary, Type type, JavaScriptSerializer serializer)

    {

        if (dictionary == null)

            throw new ArgumentNullException("dictionary");


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

    }


    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)

    {

        throw new NotImplementedException();

    }


    public override IEnumerable<Type> SupportedTypes

    {

        get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }

    }


    #region Nested type: DynamicJsonObject


    private sealed class DynamicJsonObject : DynamicObject

    {

        private readonly IDictionary<string, object> _dictionary;


        public DynamicJsonObject(IDictionary<string, object> 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<string, object>)

                {

                    new DynamicJsonObject((IDictionary<string, object>)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<string, object>)

                            new DynamicJsonObject((IDictionary<string, object>)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<string, object>;

            if (dictionary != null)

                return new DynamicJsonObject(dictionary);


            var arrayList = result as ArrayList;

            if (arrayList != null && arrayList.Count > 0)

            {

                return arrayList[0] is IDictionary<string, object> 

                    ? new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x))) 

                    : new List<object>(arrayList.Cast<object>());

            }


            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)

Answer is