views:

614

answers:

3

I'm trying to deserialize json to an object model where the collections are represented as IList<T> types.

The actual deserializing is here:

JavaScriptSerializer serializer = new JavaScriptSerializer();

return serializer.Deserialize<IList<Contact>>(
    (new StreamReader(General.GetEmbeddedFile("Contacts.json")).ReadToEnd()));

Before i post the exception i'm getting you should know what the implicit conversions are. This is the Contact type:

public class Contact
{
    public int ID { get; set; }
    public string Name { get; set; }
    public LazyList<ContactDetail> Details { get; set; }
    //public List<ContactDetail> Details { get; set; }
}

And this is the ContactDetail type:

public class ContactDetail
{
    public int ID { get; set; }
    public int OrderIndex { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
}

The important thing to know with the LazyList<T> is that it implements IList<T>:

public class LazyList<T> : IList<T>
{
    private IQueryable<T> _query = null;
    private IList<T> _inner = null;
    private int? _iqueryableCountCache = null;


    public LazyList()
    {
        this._inner = new List<T>();
    }

    public LazyList(IList<T> inner)
    {
        this._inner = inner;
    }

    public LazyList(IQueryable<T> query)
    {
        if (query == null)
            throw new ArgumentNullException();
        this._query = query;
    }

Now this LazyList<T> class definition was fine until i tried deserializing Json into it. The System.Web.Script.Serialization.JavaScriptSerializer seems to want to serialize lists to List<T> which makes sense coz of it's age but i need them in the type IList<T> so they will cast into my LazyList<T> (at least that's where i think i am going wrong).

I get this exception:

System.ArgumentException: Object of type 'System.Collections.Generic.List`1[ContactDetail]' cannot be converted to type 'LazyList`1[ContactDetail]'..

When i try using List<ContactDetail> in my Contact type (as you can see commented above) it seems to work. But i dont want to use List<T>'s. I even tried having my LazyList<T> inheriting from List<T> which seemed to execute but passing the List<T>'s internal T[] to my implementation was a nightmare and i simply don't want the bloat of List<T> anywhere in my model.

I also tried some other json libraries to no avail (it's possible i may not be using these to their full potential. I more or less replaced the references and attempted to repeat the code quoted at the top of this question. Maybe passing settings params will help??).

I dont know what to try now. Do i go with another deserializer? Do i tweak the deserializing itself? Do i need to change my types to please the deserializer? Do i need to worry more about implicit casting or just implement another interface?

A: 

It is not possible to deserialize directly to an interface, as interfaces are simply a contract. The JavaScriptSerializer has to deserialize to some concrete type that implements IList<T>, and the most logical choice is List<T>. You will have to convert the List to a LazyList, which given the code you posted, should be easy enough:

var list = serializer.Deserialize<IList<Contact>>(...);
var lazyList = new LazyList(list);
jrista
I think i get mostly what you said until the code example at the bottom. Its in the first line of the sample you posted that it will fail. Unless you can suggest a change to my LazyList class that will make it not fail?
cottsak
A: 

Unfortunately you will probably need to fix your class, as there is no way for a deserializer to know that it should be of type IList, since List is an implementation of IList.

Since the deserializers at http://json.org have source available you could just modify one to do what you want.

James Black
What do you mean "fix your class"? Can you be more specific? Should i be modding my class or the deserializer source? ..or both?
cottsak
Extend List, not ILIst for the LazySerializer. That would be a better option than doing the source for the serializer, but, if you can't change your source then you would need to find something else to change.
James Black
A: 

I ended up using the Json.NET lib which has good linq support for custom mapping. This is what my deserializing ended up looking like:

        JArray json = JArray.Parse(
            (new StreamReader(General.GetEmbeddedFile("Contacts.json")).ReadToEnd()));

        IList<Contact> tempContacts = (from c in json
                                       select new Contact
                                       {
                                           ID = (int)c["ID"],
                                           Name = (string)c["Name"],
                                           Details = new LazyList<ContactDetail>(
                                               (
                                                   from cd in c["Details"]
                                                   select new ContactDetail
                                                   {
                                                       ID = (int)cd["ID"],
                                                       OrderIndex = (int)cd["OrderIndex"],
                                                       Name = (string)cd["Name"],
                                                       Value = (string)cd["Value"]
                                                   }
                                                ).AsQueryable()),
                                           Updated = (DateTime)c["Updated"]
                                       }).ToList<Contact>();

        return tempContacts;
cottsak