Is this proper usage of IReferenceResolver inside a JsonConverter? - c#

I'm using JSON.NET to deserialize JSON (which I don't control) that represents an Event of a specific type with inner data:
{
"id": "abc",
"type": "a",
"data": {
// Data specific to "type"="a".
"a": 1
}
}
and
{
"id": "def",
"type": "b",
"data": {
// Data specific to "type"="b".
"b": 1
}
}
This JSON should be deserialized to the following classes:
public class Event
{
public string Id { get; }
public string Type { get; }
public EventDataBase Data { get; }
public Event(string id, string type, EventDataBase data)
{
this.Id = id;
this.Type = type;
this.Data = data;
}
}
public abstract class EventDataBase
{
}
public class AEventData : EventDataBase
{
public string A { get; }
public AEventData(string a)
{
this.A = a;
}
}
public class BEventData : EventDataBase
{
public string B { get; }
public BEventData(string b)
{
this.B = b;
}
}
The correct inherited class of EventDataBase should be instantiated when deserializing the Event.
There are many solutions to this problem, but they usually involve having the type belong on the data, not the parent. And if it does belong on the parent, the answer is to usually convert the JSON for Event as a JObject and then manually deserialize it. While this is possible, it feels like a hack to leverage only part of JSON.NET to do the deserialization.
The solution I've come up uses IReferenceResolver to add a reference to the type, and then when attempting to deserialize the data, get the reference to the parent type, and use it to determine the concrete class that needs to be deserialized:
public class CustomContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (member.DeclaringType == typeof(Event) && member.Name == nameof(Event.Type))
{
property.Order = Int32.MinValue;
property.MemberConverter = new EventTypeConverter();
}
else if (member.DeclaringType == typeof(Event) && member.Name == nameof(Event.Data))
{
property.Order = Int32.MaxValue;
property.MemberConverter = new EventDataConverter();
}
return property;
}
private class EventTypeConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var eventType = reader.Value as string;
serializer.ReferenceResolver.AddReference(serializer, "parentEvent.type", eventType);
return eventType;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
private class EventDataConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(EventDataBase);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var eventType = serializer.ReferenceResolver.ResolveReference(serializer, "parentEvent.type") as string;
var eventDataType = GetEventDataTypeFromEventType(eventType);
return serializer.Deserialize(reader, eventDataType);
}
private static Type GetEventDataTypeFromEventType(string eventType)
{
switch (eventType?.ToLower())
{
case "a":
return typeof(AEventData);
case "b":
return typeof(BEventData);
default:
throw new InvalidOperationException();
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
Here's a .NET Fiddle sample of this in action.
Is this valid use of the IReferenceResolver? Does JSON.NET clear the reference resolver during each call deserialize (so that multiple calls don't store the old reference)? Is it thread-safe?

Related

Use JsonConverter to deserialize JToken to bool

I have an odd JSON object I need to deserialize.
{
"data": {
"id": 123,
"permissions": {
"appName": {
"data": {
"1": {
"2021-08-01": {
"2020": {
"isAllowed": {}
}
}
}
}
}
}
}
}
I've gotten most of it to work with a wrapper and nested dictionaries but the final part isn't working as expected. It works fine if I create a class like this, but using it is clunky.
public class DataWrapper<T>
{
public T Data { get; set;}
}
public class PermissionValues
{
public JToken IsAllowed { get; set; }
}
DataWrapper<Dictionary<int, Dictionary<DateTime, Dictionary<int, PermissionValues>>>>
I'd like to change JToken to a bool. When I do that and add a JsonConverter
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return true; //{} is true but JSON.net parses as null, old was: reader.Value != null;
}
I get an error:
Newtonsoft.Json.JsonSerializationException: 'Could not convert string
'isAllowed' to dictionary key type 'System.Int32'. Create a
TypeConverter to convert from the string to the key type object. Path
'data.permissions.appName.data.1.2021-08-01.2020.isAllowed'
Any idea what I am missing?
Use JToken.ToObject()
string json = "{\"data\":{\"id\":123,\"permissions\":{\"appName\":{\"data\":{\"1\":{\"2021-08-01\":{\"2020\":{\"isAllowed\":{}}}}}}}}}";
var jToken= JToken.Parse(json).SelectToken("$.data.permissions.appName");
var result = jToken.ToObject<DataWrapper<Dictionary<int, Dictionary<DateTime, Dictionary<int, PermissionValues>>>>>();
Use the JsonConvertor Attribute on IsAllowed Property.
public class DataWrapper<T>
{
public T Data { get; set; }
}
public class PermissionValues
{
[JsonConverter(typeof(BoolConverter))]
public bool IsAllowed { get; set; }
}
public class BoolConverter : JsonConverter
{
//Implement other abstract functions
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return reader.Value != null;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(bool);
}
}

Serializing object to JSON - Get attribute Name from a field of serializing class

I have a ParameterInfo class.
[JsonObject(MemberSerialization.OptIn)]
public class ParameterInfo
{
public Parameter Param { get; protected set; }
[JsonProperty(Order = 0)]
public string Name { get => GetCultureName(); }
[JsonProperty(Order = 1)]
public string Value { get => Param.AsString(); }
}
I need to serialize my class to the json in such a way:
{"PHY_MATERIAL_PARAM_EXP_COEFF2": 0.0}
I can only get this instead
{
"Name": "PHY_MATERIAL_PARAM_EXP_COEFF2",
"Value": 0.0
}
how can I do it using Newton Json with? I am looking for a way with highest performance.
You can try to write your custom JsonConverter:
public class ParameterInfoJsonConverter : Newtonsoft.Json.JsonConverter
{
public override bool CanConvert(Type objectType) => objectType == typeof(ParameterInfo);
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer) => throw new NotImplementedException();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null) return;
writer.WriteStartObject();
var val = (ParameterInfo)value;
writer.WritePropertyName(val.Name);
writer.WriteValue(val.Value);
writer.WriteEndObject();
}
}
And either mark class with [JsonConverter(typeof(ParameterInfoJsonConverter))] attribute or pass it to JsonConvert:
JsonConvert.SerializeObject(new ParameterInfo(), new ParameterInfoJsonConverter())

Custom JsonConverter not called for nested property

I have a class Employee where Manager property itself is of type Employee
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Employee Manager { get; set; }
public IList<string> Roles { get; set; }
}
I want to create a custom JsonConverter for Employee type.
public class TestJsonConverter : JsonConverter
{
public TestJsonConverter()
{
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
IList<string> propertyNames = o.Properties().Select(p => p.Name).ToList();
o.AddFirst(new JProperty("Keys", new JArray(propertyNames)));
o.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Employee));
}
public override bool CanRead
{
get { return false; }
}
}
The ContractResolver is
class ContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
if (objectType == typeof(Employee))
{
contract.Converter = new TestJsonConverter();
}
return contract;
}
}
When I try to serialize the Employee object, custom JsonConverter is getting called only for the top level Employee object, not for the nested Manager property which is also of Employee type:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new ContractResolver();
string json = JsonConvert.SerializeObject(employee, Formatting.Indented, settings);
I think you run into the same issue described in
Custom JsonConverter WriteJson Does Not Alter Serialization of Sub-properties:
The reason that your converter is not getting applied to your child objects is because JToken.FromObject() uses a new instance of the serializer internally, which does not know about your converter.
The solution provided should be adaptable to your case.
So the JsonConverter runs once, starting at the root node.
You will need to navigate this JSon Object tree and update it yourself.
Not sure it this is what you intended to achieve but I've tried it myself and you can decide if this works for you or not.
public class TestJsonConverter : JsonConverter
{
public TestJsonConverter()
{
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject root = (JObject)t;
var stack = new Stack<JObject>();
stack.Push(root);
while (stack.Any())
{
var current = stack.Pop();
var propertyNames = current.Properties().Select(p => p.Name).ToArray();
current.AddFirst(new JProperty("Keys", new JArray(propertyNames)));
var nestedObjects = current.Properties().Where(p => p.Value.Type == JTokenType.Object).ToArray();
foreach (var nestedObj in nestedObjects)
{
stack.Push((JObject)nestedObj.Value);
}
}
root.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Employee));
}
public override bool CanRead
{
get { return false; }
}
}

Deserialize JSON array of arrays

I have the following JSON data which I'd like to deserialize to a C# POCO object but I'm having trouble deserializing the array of arrays.
var json = #"{
""name"": ""Foo"",
""pages"": [
{
""page"": 1,
""fields"": [
{
""name"": ""stuffs"",
""rows"": [
[{ ""value"" : ""$199""}, { ""value"": ""foo"" }],
[{ ""value"" : ""$222""}, { ""value"": ""bar"", ""color"": ""blue"" }]
]
}]
}
]
}";
The exception is
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'UserQuery+TableRow' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path 'rows[0]', line 4, position 5.
Following the advise of the exception message, I did attempt all of those things but only to be faced with more errors.
These are my POCO objects
public class Document
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("pages")]
public Page[] Pages { get; set; }
}
public class Page
{
[JsonProperty("page")]
public int PageNumber { get; set; }
[JsonProperty("fields")]
public FieldBase[] FieldsBase { get; set; }
}
public class TableRow
{
public Cell[] Cells { get; set; }
}
public class Cell
{
[JsonProperty("value")]
public string Value { get; set; }
[JsonProperty("color")]
public string Color { get; set; }
}
public abstract class FieldBase
{
[JsonProperty("name")]
public string Name { get; set; }
}
public class Table : FieldBase
{
[JsonProperty("rows")]
public TableRow[] Rows { get; set; } = new TableRow[0];
}
And my field converter to deal with the abstract class (not sure if this matters)
public class FieldConverter : JsonConverter
{
static JsonSerializerSettings SpecifiedSubclassConversion = new JsonSerializerSettings() { ContractResolver = new BaseSpecifiedConcreteClassConverter() };
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(FieldBase));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
return JsonConvert.DeserializeObject<Table>(jo.ToString(), SpecifiedSubclassConversion);
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException(); // won't be called because CanWrite returns false
}
}
public class BaseSpecifiedConcreteClassConverter : DefaultContractResolver
{
protected override JsonConverter ResolveContractConverter(Type objectType)
{
if (typeof(FieldBase).IsAssignableFrom(objectType) && !objectType.IsAbstract)
return null; // pretend TableSortRuleConvert is not specified (thus avoiding a stack overflow)
return base.ResolveContractConverter(objectType);
}
}
And the following line of code which, when executed in LINQPad, produces the error
JsonConvert.DeserializeObject<Document>(json, new FieldConverter()).Dump();
Any help would be greatly appreciated.
In your json, "rows" is a jagged array:
"rows": [[{ "value" : "$199"}, { "value": "foo" }]]
However, in your object model this corresponds to an array of TableRow classes that contain an array of cells. Thus you will need another JsonConverter to serialize each TableRow as an array of cells and not an object:
public class TableRowConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(TableRow);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var cells = serializer.Deserialize<Cell[]>(reader);
return new TableRow { Cells = cells };
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var row = (TableRow)value;
serializer.Serialize(writer, row.Cells);
}
}
public class JsonDerivedTypeConverter<TBase, TDerived> : JsonConverter where TDerived : TBase
{
public JsonDerivedTypeConverter()
{
if (typeof(TBase) == typeof(TDerived))
throw new InvalidOperationException("TBase and TDerived cannot be identical");
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(TBase);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<TDerived>(reader);
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, to deserialize, do:
var settings = new JsonSerializerSettings
{
Converters = new JsonConverter[] { new TableRowConverter(), new JsonDerivedTypeConverter<FieldBase, Table>() },
};
var doc = JsonConvert.DeserializeObject<Document>(json, settings);
Example fiddle.

Deserializing JSON to abstract class

I am trying to deserialize a JSON string to a concrete class, which inherits from an abstract class, but I just can't get it working. I have googled and tried some solutions but they don't seem to work either.
This is what I have now:
abstract class AbstractClass { }
class ConcreteClass { }
public AbstractClass Decode(string jsonString)
{
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.TypeNameHandling = TypeNameHandling.All;
return (AbstractClass)JsonConvert.DeserializeObject(jsonString, null, jss);
}
However, if I try to cast the resulting object, it just doesn't work.
The reason why I don't use DeserializeObject is that I have many concrete classes.
Any suggestions?
I am using Newtonsoft.Json
One may not want to use TypeNameHandling (because one wants more compact json or wants to use a specific name for the type variable other than "$type"). Meanwhile, the customCreationConverter approach will not work if one wants to deserialize the base class into any of multiple derived classes without knowing which one to use in advance.
An alternative is to use an int or other type in the base class and define a JsonConverter.
[JsonConverter(typeof(BaseConverter))]
abstract class Base
{
public int ObjType { get; set; }
public int Id { get; set; }
}
class DerivedType1 : Base
{
public string Foo { get; set; }
}
class DerivedType2 : Base
{
public string Bar { get; set; }
}
The JsonConverter for the base class can then deserialize the object based on its type. The complication is that to avoid a stack overflow (where the JsonConverter repeatedly calls itself), a custom contract resolver must be used during this deserialization.
public class BaseSpecifiedConcreteClassConverter : DefaultContractResolver
{
protected override JsonConverter ResolveContractConverter(Type objectType)
{
if (typeof(Base).IsAssignableFrom(objectType) && !objectType.IsAbstract)
return null; // pretend TableSortRuleConvert is not specified (thus avoiding a stack overflow)
return base.ResolveContractConverter(objectType);
}
}
public class BaseConverter : JsonConverter
{
static JsonSerializerSettings SpecifiedSubclassConversion = new JsonSerializerSettings() { ContractResolver = new BaseSpecifiedConcreteClassConverter() };
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Base));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
switch (jo["ObjType"].Value<int>())
{
case 1:
return JsonConvert.DeserializeObject<DerivedType1>(jo.ToString(), SpecifiedSubclassConversion);
case 2:
return JsonConvert.DeserializeObject<DerivedType2>(jo.ToString(), SpecifiedSubclassConversion);
default:
throw new Exception();
}
throw new NotImplementedException();
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException(); // won't be called because CanWrite returns false
}
}
That's it. Now you can use serialize/deserialize any derived class. You can also use the base class in other classes and serialize/deserialize those without any additional work:
class Holder
{
public List<Base> Objects { get; set; }
}
string json = #"
[
{
""Objects"" :
[
{ ""ObjType"": 1, ""Id"" : 1, ""Foo"" : ""One"" },
{ ""ObjType"": 1, ""Id"" : 2, ""Foo"" : ""Two"" },
]
},
{
""Objects"" :
[
{ ""ObjType"": 2, ""Id"" : 3, ""Bar"" : ""Three"" },
{ ""ObjType"": 2, ""Id"" : 4, ""Bar"" : ""Four"" },
]
},
]";
List<Holder> list = JsonConvert.DeserializeObject<List<Holder>>(json);
string serializedAgain = JsonConvert.SerializeObject(list);
Debug.WriteLine(serializedAgain);
I would suggest to use CustomCreationConverter in the following way:
public enum ClassDiscriminatorEnum
{
ChildClass1,
ChildClass2
}
public abstract class BaseClass
{
public abstract ClassDiscriminatorEnum Type { get; }
}
public class Child1 : BaseClass
{
public override ClassDiscriminatorEnum Type => ClassDiscriminatorEnum.ChildClass1;
public int ExtraProperty1 { get; set; }
}
public class Child2 : BaseClass
{
public override ClassDiscriminatorEnum Type => ClassDiscriminatorEnum.ChildClass2;
}
public class BaseClassConverter : CustomCreationConverter<BaseClass>
{
private ClassDiscriminatorEnum _currentObjectType;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jobj = JObject.ReadFrom(reader);
_currentObjectType = jobj["Type"].ToObject<ClassDiscriminatorEnum>();
return base.ReadJson(jobj.CreateReader(), objectType, existingValue, serializer);
}
public override BaseClass Create(Type objectType)
{
switch (_currentObjectType)
{
case ClassDiscriminatorEnum.ChildClass1:
return new Child1();
case ClassDiscriminatorEnum.ChildClass2:
return new Child2();
default:
throw new NotImplementedException();
}
}
}
try something like this
public AbstractClass Decode(string jsonString)
{
var jss = new JavaScriptSerializer();
return jss.Deserialize<ConcreteClass>(jsonString);
}
UPDATE
for this scenario methinks all work as you want
public abstract class Base
{
public abstract int GetInt();
}
public class Der:Base
{
int g = 5;
public override int GetInt()
{
return g+2;
}
}
public class Der2 : Base
{
int i = 10;
public override int GetInt()
{
return i+17;
}
}
....
var jset = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All };
Base b = new Der()
string json = JsonConvert.SerializeObject(b, jset);
....
Base c = (Base)JsonConvert.DeserializeObject(json, jset);
where c type is test.Base {test.Der}
UPDATE
#Gusman suggest use TypeNameHandling.Objects instead of TypeNameHandling.All. It is enough and it will produce a less verbose serialization.
Actually, as it has been stated in an update, the simplest way (in 2019) is to use a simple custom pre-defined JsonSerializerSettings, as explained here
string jsonTypeNameAll = JsonConvert.SerializeObject(priceModels, Formatting.Indented,new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
And for deserializing :
TDSPriceModels models = JsonConvert.DeserializeObject<TDSPriceModels>(File.ReadAllText(jsonPath), new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
public class CustomConverter : JsonConverter
{
private static readonly JsonSerializer Serializer = new JsonSerializer();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var typeString = jObject.Value<string>("Kind"); //Kind is a property in json , from which we know type of child classes
var requiredType = RecoverType(typeString);
return Serializer.Deserialize(jObject.CreateReader(), requiredType);
}
private Type RecoverType(string typeString)
{
if (typeString.Equals(type of child class1, StringComparison.OrdinalIgnoreCase))
return typeof(childclass1);
if (typeString.Equals(type of child class2, StringComparison.OrdinalIgnoreCase))
return typeof(childclass2);
throw new ArgumentException("Unrecognized type");
}
public override bool CanConvert(Type objectType)
{
return typeof(Base class).IsAssignableFrom(objectType) || typeof((Base class) == objectType;
}
public override bool CanWrite { get { return false; } }
}
Now add this converter in JsonSerializerSettings as below
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
jsonSerializerSettings.Converters.Add(new CustomConverter());
After adding serialize or deserialize base class object as below
JsonConvert.DeserializeObject<Type>("json string", jsonSerializerSettings );
I had a similar issue, and I solved it with another way, maybe this would help someone:
I have json that contains in it several fields that are always the same, except for one field called "data" that can be a different type of class every time.
I would like to de-serialize it without analayzing every filed specific.
My solution is:
To define the main class (with 'Data' field) with , the field Data is type T.
Whenever that I de-serialize, I specify the type:
MainClass:
public class MainClass<T>
{
[JsonProperty("status")]
public Statuses Status { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("data")]
public T Data { get; set; }
public static MainClass<T> Parse(string mainClsTxt)
{
var response = JsonConvert.DeserializeObject<MainClass<T>>(mainClsTxt);
return response;
}
}
User
public class User
{
[JsonProperty("id")]
public int UserId { get; set; }
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
}
Product
public class Product
{
[JsonProperty("product_id")]
public int ProductId { get; set; }
[JsonProperty("product_name")]
public string ProductName { get; set; }
[JsonProperty("stock")]
public int Stock { get; set; }
}
Using
var v = MainClass<User>.Parse(userJson);
var v2 = MainClass<Product>.Parse(productJson);
json example
userJson: "{"status":1,"description":"my description","data":{"id":12161347,"first_name":"my fname","last_name":"my lname"}}"
productJson: "{"status":1,"description":"my description","data":{"product_id":5,"product_name":"my product","stock":1000}}"
public abstract class JsonCreationConverter<T> : JsonConverter
{
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T) == objectType;
}
public override object ReadJson(JsonReader reader,Type objectType,
object existingValue, JsonSerializer serializer)
{
try
{
var jObject = JObject.Load(reader);
var target = Create(objectType, jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
catch (JsonReaderException)
{
return null;
}
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Now implement this interface
public class SportActivityConverter : JsonCreationConverter<BaseSportActivity>
{
protected override BaseSportActivity Create(Type objectType, JObject jObject)
{
BaseSportActivity result = null;
try
{
switch ((ESportActivityType)jObject["activityType"].Value<int>())
{
case ESportActivityType.Football:
result = jObject.ToObject<FootballActivity>();
break;
case ESportActivityType.Basketball:
result = jObject.ToObject<BasketballActivity>();
break;
}
}
catch(Exception ex)
{
Debug.WriteLine(ex);
}
return result;
}
}

Categories

Resources