How to deserialize an abstract class? [duplicate] - c#

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;
}
}

Related

Is it possible to get the properties of JsonReader before reading?

I am stuck at a problem with this, my current base API class implements getting an object from an API like this:
HttpResponceMessage GetResponce(string path) => Task.Run(async() => await client.GetAsync(path));
T GetTFromRequest<T>(string path, JsonConverter[] converters) =>
Task.Run(async() => await GetResponce(path).Content.ReadAsAsync<T>(converters); // Converters gets put into a new JsonMediaTypeFormatter
These are used in other classes to make quick methods. Class Example:
public class ExampleAPI : BaseAPI
{
private static readonly JsonConverter[] converters = new[] { new RecordCoverter() };
public string APIKey { get; set; }
public ExampleData GetExampleData(/* params here such as string apiKey */) =>
GetTFromRequset<ExampleData>($"examplepath?key={APIKey}", converters);
}
Example data would be something like this:
{
"success":true,
"records":[
{
"Foo":"Bar",
"Data":"Dahta"
}
]
}
So the classes would look like this:
public class RecordConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => objectType == typeof(IRecord);
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)
{
throw new NotImplementedException();
}
}
public interface IRecord {}
public class ExampleData
{
public bool success { get; set; } = true;
public IRecord[] records { get; set; } = new[] {};
}
public class DataRecord : IRecord
{
public string Foo { get; set; }
public string Data { get; set; }
}
public class FaltyRecord : IRecord
{
public string Bar { get; set; }
public string Dahta { get; set; }
}
The issue that keeps arising that I can't figure out if the objects in "records" is FaltyRecords or DataRecords without doing reader.Read() which would just throw off just being about to put the following into RecordConverter:
if(reader.NextProperties().All(x => DataRecordProps.Contains(x)))
return serializer.Deserialize<DataExample>(reader);
if(reader.NextProperties().All(x => FaltyRecordProps.Contains(x)))
return serializer.Deserialize<FaltyRecord>(reader);
if(...)
return ...;
I know that in XmlReader there's the method GetSubtree() so I can do var propReader = reader.GetSubtree(); then use propReader as much as I want and just do retrun (IRecord[])serializer.Deserialize(reader);. I also see using JObject a lot but I don't see a JObject(reader) method.
Use JObject.Load(reader) method.
var jObj = JObject.Load(reader);
var properties = jObj.Properties();
// code here to find the type
return jObj.ToObject(type);

JsonConvert deserialize an array of abstract classes

Let's say I have the following class structure (Building is an abstract class):
public class Street
{
public string StreetName { get; set; }
public Building[] Buildings { get; set; }
}
public abstract class Building
{
public string Name { get; set; }
}
public class House : Building
{
public int Floors { get; set; }
}
public class Flat : Building
{
public int WhichFloor { get; set; }
}
I then create a street object with a few flats in the buildings array:
Flat f1 = new Flat { Name = "Flat 1", WhichFloor = 1 };
Flat f2 = new Flat { Name = "Flat 2", WhichFloor = 2 };
Street street = new Street
{
StreetName = "Street Name",
Buildings = new[] { f1, f2 }
};
Using JsonConvert I then Serialize the object:
var toJson = JsonConvert.SerializeObject(street);
Now I want to convert the json back to a street object:
var fromJson = JsonConvert.DeserializeObject<Street>(toJson);
This fails with the following error:
"Could not create an instance of type Building. Type is an interface or abstract class and cannot be instantiated. Path 'Buildings[0].WhichFloor'"
How can I tell the JsonConvert class that Buildings should be an array of flats?
As per #Evk's shared link, you should try setting TypeNameHandling to TypeNameHandling.Auto while serializing and deserializing:
var toJson = JsonConvert.SerializeObject(street, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
var fromJson = JsonConvert.DeserializeObject<Street>(toJson, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
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;
}
}

JSON infer class type based on Dictionary key

I would like to deserialize a Dictionary<string, AbstractDataObject>. AbstractDataObject is an abstract class which SomeClass1 and SomeClass2 inherit from. The key value of the dictionary (a string) is the name of the class:
{
"StoredData": {
"SomeClass1": {
"Foo": "Test"
},
"SomeClass2": {
"Bar": 123
}
}
}
The classes look like following (simplified):
public class Data
{
public Dictionary<string, AbstractDataObject> StoredData { get; set; }
}
public abstract class AbstractDataObject
{
}
public class SomeClass1 : AbstractDataObject
{
public string Foo { get; set; }
}
public class SomeClass2 : AbstractDataObject
{
public int Bar { get; set; }
}
How can I tell Json.NET to infer the class type based on the Dictionary key? I've looked around but I'm unsure where to start implementing this functionality
You could make a custom JsonConverter to handle this:
public class CustomDictionaryConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Dictionary<string, AbstractDataObject>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var dict = new Dictionary<string, AbstractDataObject>();
JObject jo = JObject.Load(reader);
foreach (JProperty prop in jo.Properties())
{
switch (prop.Name)
{
case "SomeClass1":
dict.Add("SomeClass1", prop.Value.ToObject<SomeClass1>(serializer));
break;
case "SomeClass2":
dict.Add("SomeClass2", prop.Value.ToObject<SomeClass2>(serializer));
break;
}
}
return dict;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then use it like this:
Data data = JsonConvert.DeserializeObject<Data>(json, new CustomDictionaryConverter());
Fiddle: https://dotnetfiddle.net/ba4wB6

Json.Net Serialization of Type with Polymorphic Child Object

We would like to be able to serialize/deserialize json from/to C# classes, with the main class having an instance of a polymorphic child object. Doing so is easy using Json.Net's TypeNameHandling.Auto setting. However, we would like to do so without the "$type" field.
The first thought is to be able to rename "$type" to a value of our choosing and to have the value for the type be an enum that would map the sub-types properly. I have not seen that as being an option but would be glad to hear if it is possible.
The second thought was along the following lines... Below is a first pass at classes, with the top level class having an indicator (SubTypeType) as to what type of data is contained in the child object (SubTypeData). I've dug around a bit into the Json.Net documentation and have tried a few things but have had no luck.
We currently have full control over the data definition, but once it is deployed, then things are locked.
public class MainClass
{
public SubType SubTypeType { get; set; }
public SubTypeClassBase SubTypeData { get; set; }
}
public class SubTypeClassBase
{
}
public class SubTypeClass1 : SubTypeClassBase
{
public string AaaField { get; set; }
}
public class SubTypeClass2 : SubTypeClassBase
{
public string ZzzField { get; set; }
}
Having the subtype information in the container class is problematic for two reasons:
The container class instance is not accessible when Json.NET is reading the contained class.
If you later need to convert the SubTypeClassBase property into, say, a list, there will be nowhere to put the subtype information.
Instead, I would recommend adding the subtype information as a property in the base class:
[JsonConverter(typeof(SubTypeClassConverter))]
public class SubTypeClassBase
{
[JsonConverter(typeof(StringEnumConverter))] // Serialize enums by name rather than numerical value
public SubType Type { get { return typeToSubType[GetType()]; } }
}
Now the custom subtype enum will be serialized whenever an object assignable to SubTypeClassBase is serialized. Having done that, for deserialization you can create a JsonConverter that loads the json for a given SubTypeClassBase into a temporary JObject, checks the value of the "Type" property, and deserializes the JSON object as the appropriate class.
Prototype implementation below:
public enum SubType
{
BaseType,
Type1,
Type2,
}
[JsonConverter(typeof(SubTypeClassConverter))]
public class SubTypeClassBase
{
static readonly Dictionary<Type, SubType> typeToSubType;
static readonly Dictionary<SubType, Type> subTypeToType;
static SubTypeClassBase()
{
typeToSubType = new Dictionary<Type,SubType>()
{
{ typeof(SubTypeClassBase), SubType.BaseType },
{ typeof(SubTypeClass1), SubType.Type1 },
{ typeof(SubTypeClass2), SubType.Type2 },
};
subTypeToType = typeToSubType.ToDictionary(pair => pair.Value, pair => pair.Key);
}
public static Type GetType(SubType subType)
{
return subTypeToType[subType];
}
[JsonConverter(typeof(StringEnumConverter))] // Serialize enums by name rather than numerical value
public SubType Type { get { return typeToSubType[GetType()]; } }
}
public class SubTypeClass1 : SubTypeClassBase
{
public string AaaField { get; set; }
}
public class SubTypeClass2 : SubTypeClassBase
{
public string ZzzField { get; set; }
}
public class SubTypeClassConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SubTypeClassBase);
}
public override bool CanWrite { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
var typeToken = token["Type"];
if (typeToken == null)
throw new InvalidOperationException("invalid object");
var actualType = SubTypeClassBase.GetType(typeToken.ToObject<SubType>(serializer));
if (existingValue == null || existingValue.GetType() != actualType)
{
var contract = serializer.ContractResolver.ResolveContract(actualType);
existingValue = contract.DefaultCreator();
}
using (var subReader = token.CreateReader())
{
// Using "populate" avoids infinite recursion.
serializer.Populate(subReader, existingValue);
}
return existingValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
You can try with JsonSubtypes converter implementation that support registering type mapping with enum values.
In your case it looks like this:
public class MainClass
{
public SubTypeClassBase SubTypeData { get; set; }
}
[JsonConverter(typeof(JsonSubtypes), "SubTypeType")]
[JsonSubtypes.KnownSubType(typeof(SubTypeClass1), SubType.WithAaaField)]
[JsonSubtypes.KnownSubType(typeof(SubTypeClass2), SubType.WithZzzField)]
public class SubTypeClassBase
{
public SubType SubTypeType { get; set; }
}
public class SubTypeClass1 : SubTypeClassBase
{
public string AaaField { get; set; }
}
public class SubTypeClass2 : SubTypeClassBase
{
public string ZzzField { get; set; }
}
public enum SubType
{
WithAaaField,
WithZzzField
}
[TestMethod]
public void Deserialize()
{
var obj = JsonConvert.DeserializeObject<MainClass>("{\"SubTypeData\":{\"ZzzField\":\"zzz\",\"SubTypeType\":1}}");
Assert.AreEqual("zzz", (obj.SubTypeData as SubTypeClass2)?.ZzzField);
}
Here's a full example that can both read and write JSON with polymorphic objects.
Assuming we have the following class structure:
public class Base {}
public class SubClass1 : Base {
public int field1;
}
public class SubClass2 : Base {
public int field2;
}
We can use a custom converter that creates an extra field in JSON named type when serializing and reads it when deserializing.
public class PolymorphicJsonConverter : JsonConverter
{
public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
JObject item = JObject.Load(reader);
var type = item["type"].Value<string>();
if (type == "SubClass1") {
return item.ToObject<SubClass1>();
} else if (type == "SubClass2") {
return item.ToObject<SubClass2>();
} else {
return null;
}
}
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer) {
JObject o = JObject.FromObject(value);
if (value is SubClass1) {
o.AddFirst(new JProperty("type", new JValue("SubClass1")));
} else if (value is SubClass1) {
o.AddFirst(new JProperty("type", new JValue("SubClass2")));
}
o.WriteTo(writer);
}
public override bool CanConvert (Type objectType) {
return typeof(Base).IsAssignableFrom(objectType);
}
}
You could use this converter in a container class like so:
public class Container {
public List<Base> items;
public string Save() {
return JsonConvert.SerializeObject(items, new PolymorphicJsonConverter())
}
public void Load(string jsonText) {
items = JsonConvert.DeserializeObject<List<Base>>(jsonText, new PolymorphicJsonConverter());
}
}
Alternatively you could use JSON.net's built in type hinting, instead of rolling your own JsonConverter, but it's not so flexible and creates very un-portable JSON.
JsonConvert.SerializeObject(items, new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Auto
});

Json.net: How to detect if a property is at input during deserialization?

I have the following class:
[DataContract]
public class MyMessage
{
[DataMember(IsRequired = false)]
public string Foo { get; set; }
[DataMember(IsRequired = false)]
public string Bar { get; set; }
public void NotifyPropertyFound( string propName )
{
...
}
}
I need to deserialize the following Json input:
{ "Foo": null,
"Melda": 123
}
I need the deserializer to call the method NotifyPropertyFound twice: once with argument "Foo" (which I do have in my class) and once again with argument "Melda" (which I don't have).
I'm playing with IContractResolver and JsonConverter, but none of them seems to provide an opportunity to call the NotifyPropertyFound method. Any idea?
EDIT
I can detect "Foo" using an IValueProvider. But I still need to detect "Melda".
Maybe you can change the property to be backed by a private field instead of an auto property. You can then try to call that method in the setter that is called by the default deserializer.
Another alternative is to write your own deserializer.
You can get the result you want using a custom JsonConverter like the following:
class MyMessageConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(MyMessage));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
MyMessage msg = jo.ToObject<MyMessage>();
foreach (JProperty prop in jo.Properties())
{
msg.NotifyPropertyFound(prop.Name);
}
return msg;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Demo:
class Program
{
static void Main(string[] args)
{
string json = #"{ ""Foo"": null, ""Melda"": 123 }";
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new MyMessageConverter());
MyMessage msg = JsonConvert.DeserializeObject<MyMessage>(json, settings);
}
}
[DataContract]
public class MyMessage
{
[DataMember(IsRequired = false)]
public string Foo { get; set; }
[DataMember(IsRequired = false)]
public string Bar { get; set; }
public void NotifyPropertyFound(string propName)
{
// Write to the console each time this method is called
Console.WriteLine(propName);
}
}
Output:
Foo
Melda

Categories