Legend
Legend

Reputation: 116990

Making the JSON deserialization work in case of Generics?

I have the following:

public class BaseEntity<T> where T: class
{
    public OperationStatus OperationStatus { set; get; }
    public List<T> List { set; get; }

    protected internal BaseEntity()
    {
        if (OperationStatus == null)
        {
            OperationStatus = new OperationStatus();
            OperationStatus.IsSuccess = true;
        }

        this.List = new List<T>();
    }

    internal BaseEntity(IEnumerable<T> list)
    {
        if (OperationStatus == null)
        {
            OperationStatus = new OperationStatus();
            OperationStatus.IsSuccess = true;
        }

        this.List = new List<T>();
        foreach (T k in list)
        {
            this.List.Add(k);
        }
    }

}

public class KeyValuePair
{
    public string key;
    public string value;
}

public class KeyValuePairList : BaseEntity<KeyValuePair>
{
    public KeyValuePairList() { }
    public KeyValuePairList(IEnumerable<KeyValuePair> list)
        : base(list) { }
}

// Multiple other classes like KeyValuePair but all have the
// same behavior so they have been derived from BaseEntity

Now in my code, I am trying to map a JSON string to an instance of KeyValuePair list and I'm currently doing it as follows:

result = 
@"{
    \"d\": {
        \"OperationStatus\": {
            \"IsSuccess\": true,
            \"ErrorMessage\": null,
            \"ErrorCode\": null,
            \"InnerException\": null
        },
        \"List\": [{
            \"key\": \"Key1\",
            "\value\": \"Value1\"
        }, {
            \"key\": \"Key2\",
            \"value\": \"Value2\"
        }]
    }
}"

Attempt #1

JavaScriptSerializer serializer = new JavaScriptSerializer();
KeyValuePairList output = serializer.Deserialize<KeyValuePairList>(result);

However, this does not work because the constructor of KeyValuePairList is not being called with any arguments. If I remove that constructor, JSON serialization fails with an error No parameterless constructor found. How can I tell KeyValuePairList to use KeyValuePair as the template in its invocation? Or maybe how can I adapt JSON serializer for this purpose?

Attempt #2

I tried JSON.net too, as follows:

var oo = JsonConvert.DeserializeObject<KeyValuePairList>(result);

Any suggestions on how to make this work?

Upvotes: 3

Views: 2518

Answers (2)

Akash Kava
Akash Kava

Reputation: 39956

Try T[] instead of List<T>

You should have two properties as,

public T[] Items{
    get{
        return ItemList.ToArray();
    }
    set{
        ItemList.Clear();
        ItemList.AddRange(value);
    }
}

[ScriptIgnore]
public List<T> ItemList {get;set;}

Items as array will be serialized in JSON and you can use ItemList for your other operations.

Upvotes: 0

Legend
Legend

Reputation: 116990

Actually the solution is simpler than I thought it would be. The problem is that the server is returning the JSON string with the root node d. Because of this the deserializing fails because it doesn't know what to do with the root node d. This can be solved as follow:

Step 1: Add an additional class JSONWrapper that wraps incoming JSON strings:

public class JSONWrapper<T> where T:class
{
    public T d {set; get;}
}

Step 2: Deserialize using this new class instead

JavaScriptSerializer serializer = new JavaScriptSerializer();
var oo = serializer.Deserialize<JsonWrapper<KeyValuePairList>>(result);

Much more in-line with my whole logic so I don't have to make any major changes. Thanks to everyone else who helped me out with their valuable time.

Upvotes: 2

Related Questions