Casting interfaces for deserialization in JSON.NET
(Copied from this question)
In cases where I have not had control over the incoming JSON (and so cannot ensure that it includes a $type property) I have written a custom converter that just allows you to explicitly specify the concrete type:
public class Model
{
[JsonConverter(typeof(ConcreteTypeConverter<Something>))]
public ISomething TheThing { get; set; }
}
This just uses the default serializer implementation from Json.Net whilst explicitly specifying the concrete type.
An overview are available on this blog post. Source code is below:
public class ConcreteTypeConverter<TConcrete> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
//assume we can convert to anything for now
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//explicitly specify the concrete type we want to create
return serializer.Deserialize<TConcrete>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//use the default serialization - it works fine
serializer.Serialize(writer, value);
}
}
@SamualDavis provided a great solution in a related question, which I'll summarize here.
If you have to deserialize a JSON stream into a concrete class that has interface properties, you can include the concrete classes as parameters to a constructor for the class! The NewtonSoft deserializer is smart enough to figure out that it needs to use those concrete classes to deserialize the properties.
Here is an example:
public class Visit : IVisit
{
/// <summary>
/// This constructor is required for the JSON deserializer to be able
/// to identify concrete classes to use when deserializing the interface properties.
/// </summary>
public Visit(MyLocation location, Guest guest)
{
Location = location;
Guest = guest;
}
public long VisitId { get; set; }
public ILocation Location { get; set; }
public DateTime VisitDate { get; set; }
public IGuest Guest { get; set; }
}
Why use a converter? There is a native functionality in Newtonsoft.Json
to solve this exact problem:
Set TypeNameHandling
in the JsonSerializerSettings
to TypeNameHandling.Auto
JsonConvert.SerializeObject(
toSerialize,
new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto
});
This will put every type into the json, that is not held as a concrete instance of a type but as an interface or an abstract class.
Make sure that you are using the same settings for serialization and deserialization.
I tested it, and it works like a charm, even with lists.
Search Results Web result with site links
⚠️ WARNING:
Only use this for json from a known and trusted source. User snipsnipsnip correctly mentioned that this is indeed a vunerability.
See CA2328 and SCS0028 for more information.
Source and an alternative manual implementation: Code Inside Blog