Reputation: 787
I have a c# 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:
value
out, and deserialize thatBaseReponse<T>
to better get at the generic inside the value
propertyI 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
Upvotes: 0
Views: 1915
Reputation: 116751
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, e.g. 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.
Upvotes: 1
Reputation: 46
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
Upvotes: 1