简体   繁体   中英

deserializing json into base object with generic type

I have ac# class that allows you to specify a generic type. I would like to be able to reuse this class to remove the noise of a json wrapper that all objects will share, a value property that holds the data I need.

If the reusable class has a property of value that gets me the first step of the way. But I'm having trouble deserializing into the generic type supplied, because when I cast it always returns null . I know this is because the actual type of value after deserialization is probably JObject or similiar, I forget the type exactly. You can see in my demo app that it does indeed grab all of the data, but I can't just cast it into my generic type, because it isn't actually that type yet.

var get1 = JsonConvert.DeserializeObject<BaseResponse<NameObj>>("{ value: {...} }");
var name1 = get1.Get();//name1 is null

var getList1 = JsonConvert.DeserializeObject<BaseResponse<NameObj>>("{ value: [{...}] }");
var nameList = getList1.List();//nameList is null

public class BaseResponse<T> where T : BaseObj
{
    public object value;

    public T Get()
    {
        return value as T;
    }

    public List<T> List()
    {
        return value as List<T>;
    }
}
public class NameObj : BaseObj
{
    public string Name { get; set; }
}

I'll have more than one type of BaseObj class.

So, I'm wondering what is better:

  1. manipulate json first to grab value out, and deserialize that
  2. fix how I'm deserializing the BaseReponse<T> to better get at the generic inside the value property

I suppose solution 1 will do for now, since all of the json follows that pattern, but I was hoping for a pure deserialize instead of having to do string manipulation.

repo here: https://github.com/nateous/JSONDeserializeBaseWithGeneric

Your problem is that, while BaseResponse<T> is typed, the underlying data member public object value is untyped, and thus Json.NET has no idea of how to deserialize it. What it does in practice is to deserialize to some type that is sufficient to capture the JSON to be deserialized, typically some JToken hierarchy.

Since you don't want this, you should make the property typed, eg like so:

public abstract class BaseResponseBase
{
    // Allows for non-generic access to the BaseResponse value if needed
    // Use a method rather than a property to prevent accidental serialization.
    public abstract object GetBaseValue();
}

public class BaseResponse<T> : BaseResponseBase
{
    public override object GetBaseValue() { return Value; }

    public T Value { get; set; }
}

Since the value does not always inherit from BaseObj (specifically, when it is a list and not an object) the generic constraint is no longer appropriate. BaseResponseBase provides non-generic access to the deserialized value. If such access is never needed, the base class can be omitted.

And then to deserialize, do:

var name = JsonConvert.DeserializeObject<BaseResponse<NameObj>>("{ value: {...} }").Value;

var nameList = JsonConvert.DeserializeObject<BaseResponse<List<NameObj>>>("{ value: [{...}] }").Value;

Incidentally, you could always just use JsonConvert.DeserializeAnonymousType instead to peel off the outer container object:

var name = JsonConvert.DeserializeAnonymousType("{ value: {...} }", 
                                                new { value = (NameObj)null })
    .value;

This is strictly preferable to using string manipulation to deserialize JSON.

To solve this:

var get1 = JsonConvert.DeserializeObject<BaseResponse<NameObj>>("{ value: {...} }");
var name1 = get1.Get();//name1 is null

Change the type of value in BaseResponse class from object to T.

public class BaseResponse<T> where T : BaseObj
{
    public T value; //from object to T

    public T Get()
    {
        return value as T;
    }

    public List<T> List()
    {
        return value as List<T>;
    }
}

Now for this:

var getList1 = JsonConvert.DeserializeObject<BaseResponse<NameObj>>("{ value: [{...}] }");
var nameList = getList1.List();//nameList is null

Deserializing ListBaseJSON returns a BaseObj array but BaseResponse class has a constraint of BaseObj. You may have to remove the constraint.

So your BaseResponse class becomes:

public class BaseResponse<T>
{
    public T value;

    public T Get()
    {
        return value;
    }

}

So, now you can:

var get1 = JsonConvert.DeserializeObject<BaseResponse<NameObj>>("{ value: {...} }");
var name1 = get1.Get();//name1 is null

var getList1 = JsonConvert.DeserializeObject<BaseResponse<NameObj>>("{ value: [{...}] }");
var nameList = getList1.List();//nameList is null

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM