Manish Garg
Manish Garg

Reputation: 85

JSON deserialize List<SomeObject> is not working

We are deserializing the JSON using C# System.Runtime.Serialization.Json.DataContractJsonSerializer. It is working for normal objects but not for List.

For example if the json string is below then it works fine:

{"CacheInsertDateTime":"\/Date(1360761324878)\/","Data":{"__type":"SomeObject:#ConsoleApplication1","Symbol":"some string"}}

But if the josn is below:

{"CacheInsertDateTime":"\/Date(1360761324878)\/","Data":[{"__type":"SomeObject:#ConsoleApplication1","Symbol":"some string"},{"__type":"SomeObject:#ConsoleApplication1","Symbol":"some other string"}]}

The data comes as List<Object> not List<SomeObject>. I attached the sample solution also, showing the same problem. Any help or direction is appreciated.

EDIT: added the code

{

namespace ConsoleApplication1 { class Program { /* Someobject class*/ [DataContract] public class SomeObject { public SomeObject(string sym) { this.Symbol = sym; } [DataMember] public string Symbol { get; set; }

    }
    /* Cahe Data */
    [DataContract()]
    [KnownType("GetKnownTypes")]
    class CacheData
    {
        [DataMember()]
        public object Data { get; set; }

        [DataMember()]
        public DateTime CacheInsertDateTime { get; set; }

        public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
        {
            return GetKnownTypes();
        }
        public static IEnumerable<Type> GetKnownTypes()
        {
            if (knownTypes == null)
            {
                // Since reflection is costly, we will do the lookup once for the known types and persist the data in knownTypes variable
                knownTypes = new List<Type>();

                // first add types from DataModel assembly get types which are marked with DataContract attribute
                var typesInCurrentAssembly = Assembly.GetExecutingAssembly().GetTypes().Where
                    (t => t.GetCustomAttributes(false).Any(attrib => attrib is DataContractAttribute));
                foreach (var type in typesInCurrentAssembly)
                {
                    // add type and list<type> also to the known types list
                    knownTypes.Add(type);
                    knownTypes.Add(typeof(List<>).MakeGenericType(type));
                }

                knownTypes.Add(typeof(DataTable));
            }
            return knownTypes;
        }
        private static List<Type> knownTypes = null;
    }

    /*Cache Response Class*/
    class CacheResponse<T> where T : class
    {
        public CacheData CacheData { get; set; }
        public T Data
        {
            get{return (CacheData != null && CacheData.Data.GetType() == typeof(T)) ? CacheData.Data as T: null;}
        }
    }
    /* Main class */
    static void Main(string[] args)
    {   
        //1. first with someobject, it works same as above
        string jsonString = "{\"CacheInsertDateTime\":\"\\/Date(1360761324878)\\/\",\"Data\":{\"__type\":\"SomeObject:#ConsoleApplication1\",\"Symbol\":\"some object 1\"}}";
        CacheData obj = null;
        byte[] byteData = Encoding.UTF8.GetBytes(jsonString);
        using (MemoryStream stream = new MemoryStream(byteData))
        {
            var serializer = new DataContractJsonSerializer(typeof(CacheData));
            obj = serializer.ReadObject(stream) as CacheData;
        }
        CacheResponse<SomeObject> response1 = new CacheResponse<SomeObject>();
        response1.CacheData = obj;
        SomeObject responseObj = response1.Data; //this response object is fine            

        //2. with list<someobject>, it does not work
        jsonString = "{\"CacheInsertDateTime\":\"\\/Date(1360761324878)\\/\",\"Data\":[{\"__type\":\"SomeObject:#ConsoleApplication1\",\"Symbol\":\"some object 1\"},{\"__type\":\"SomeObject:#ConsoleApplication1\",\"Symbol\":\"some object 2\"}]}";
        byteData = Encoding.UTF8.GetBytes(jsonString);
        using (MemoryStream stream = new MemoryStream(byteData))
        {
            var serializer = new DataContractJsonSerializer(typeof(CacheData));
            obj = serializer.ReadObject(stream) as CacheData;
        }
        CacheResponse<List<SomeObject>> response2 = new CacheResponse<List<SomeObject>>();
        response2.CacheData = obj;
        List<SomeObject> responseList = response2.Data;//this is null            
    }
}

}

Upvotes: 1

Views: 618

Answers (1)

Andre Loker
Andre Loker

Reputation: 8408

Make sure to let the serializer know what types to expect in the list, for example by passing it to the constructor of DataContractJsonSerializer.

The following example works as expected:

namespace ConsoleApplication1
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.Serialization.Json;
    using System.Text;

    public class SomeObject
    {
        public string Symbol { get; set; }
    }

    public class MyClass
    {
        public DateTime CacheInsertTime { get; set; }
        public List<object> Data { get; set; }
    }

    public class Program
    {
        private const string JsonString = @"{""CacheInsertDateTime"":""\/Date(1360761324878)\/"",""Data"":[{""__type"":""SomeObject:#ConsoleApplication1"",""Symbol"":""some string""},{""__type"":""SomeObject:#ConsoleApplication1"",""Symbol"":""some other string""}]}";

        private static void Main()
        {
            var ser = new DataContractJsonSerializer(typeof (MyClass), new[] {typeof (SomeObject)});
            var ms = new MemoryStream(Encoding.ASCII.GetBytes(JsonString));

            var obj = (MyClass) ser.ReadObject(ms);
            Trace.Assert(obj.Data.Count == 2);
            Trace.Assert(((SomeObject) obj.Data[1]).Symbol == "some other string");
        }
    }
}

Note how I pass typeof(SomeType) to the constructor of the serializer.

Upvotes: 0

Related Questions