I'm using the Newtonsoft.JSON library to serialize several objects. In some cases I don't want to serialize a property so I've used the ShouldSerialize prefix which has been largely successful in most cases. In one case I only want to serialize a property if it belongs to a specific class.
I've tried using the stack trace but it only tells me that the JSON object is calling the ShouldSerialize method. I don't need to know what calls ShouldSerialize, I need to know what parent class ShouldSerialize belongs to such as Parent.Child.ShouldSerialize.
How can I determine what the parent class name is while using the JSON object using the code sample below?
class Foo
{
public SharedClass SomeProperty
{
get;
set;
}
}
class Bar
{
public SharedClass SomeProperty
{
get;
set;
}
}
class SharedClass
{
public string SomeValue
{
get;
set;
}
public bool ShouldSerializeSomeValue
{
//pseudo logic
return ClassName == "Foo";
}
}
As was pointed out by Lasse Karlsen in the comments, if your SharedClass does not have a reference to its parent, there is no way for the ShouldSerializeSomeValue() method in that class to know what the parent class is.
However, if you are using Json.Net 6.0 Release 6 or later, you can work around this by using a custom JsonConverter as a means to selectively omit properties from the shared class (instead of using a ShouldSerialize() method), and then place [JsonConverter] attributes on the SharedClass properties within the appropriate parent classes to indicate which properties should be omitted for that instance.
Here is how the updated example class definitions might look. You'll notice I've marked the SharedClass instance on Foo to indicate it should use a custom converter called OmitPropertiesConverter to omit the SomeValue property. The SharedClass instance on Bar does not use a converter, so that instance will be serialized as normal.
class Foo
{
[JsonConverter(typeof(OmitPropertiesConverter), "SomeValue")]
public SharedClass Shared { get; set; }
}
class Bar
{
public SharedClass Shared { get; set; }
}
class SharedClass
{
public string SomeValue { get; set; }
public string SomeOtherValue { get; set; }
}
Below is the code for the OmitPropertiesConverter. Its constructor accepts a propsToOmit string which is a comma-delimited list of property names to be excluded from the serialization. This gets split into an array for later use in the WriteJson method. The WriteJson method takes the SharedClass value, converts it to a JObject, then programmatically removes the properties which are in the propsToOmit array before writing the JObject to the JsonWriter.
class OmitPropertiesConverter : JsonConverter
{
string[] propsToOmit;
public OmitPropertiesConverter(string propsToOmit)
{
this.propsToOmit = propsToOmit.Split(new char[] {','},
StringSplitOptions.RemoveEmptyEntries);
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(SharedClass));
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
JObject jo = JObject.FromObject(value, serializer);
// Note: ToList() is needed here to prevent "collection was modified" error
foreach (JProperty prop in jo.Properties()
.Where(p => propsToOmit.Contains(p.Name))
.ToList())
{
prop.Remove();
}
jo.WriteTo(writer);
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Here is a simple demo program which shows the converter in action:
class Program
{
static void Main(string[] args)
{
var root = new
{
Foo = new Foo
{
Shared = new SharedClass
{
SomeValue = "foo1",
SomeOtherValue = "foo2"
}
},
Bar = new Bar
{
Shared = new SharedClass
{
SomeValue = "bar1",
SomeOtherValue = "bar2"
}
}
};
string json = JsonConvert.SerializeObject(root, Formatting.Indented);
Console.WriteLine(json);
}
}
And here is the output of the above demo. You'll notice that the SomeValue property on the SharedClass instance inside Foo is not included in the output, but it is included on the instance inside Bar.
{
"Foo": {
"Shared": {
"SomeOtherValue": "foo2"
}
},
"Bar": {
"Shared": {
"SomeValue": "bar1",
"SomeOtherValue": "bar2"
}
}
}
I want to decorate my classes with custom attributes, and read them when I convert to json using json.net inside a custom JsonConverter. I'll then vary the serialization depending on this custom attribute.
public class MyCustomJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//I want to get any attributes set on the property here.
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Int64);
}
}
Another approach would be to specify my custom JsonConverter on the property using an attribute, but I don't want to do this because I want to inject some behaviour into the constructor of my custom JsonConverter by instantiating the converters in the JsonSerializer settings as below.
String json = JsonConvert.SerializeObject(new MyCLass(), new JsonSerializerSettings
{
Converters = new List
{
new MyCustomJsonConverter()
}
});
I can get to the name of the property in the textWriter path. And I can see some interesting hints in the documentation about Metadata, but I can't find a way to do this.
Here's an example decorated class:
public class MyCustomAttribute : Attribute { }
public class MyCLass
{
[MyCustom]
public Int64 MyInt { get; set; }
}
JsonConverters apply to types, not to fields or properties.
Instead of adding an attribute to a property that uses an existing type, consider creating a new type and writing a convert for that instead.
public struct MyCustomType
{
...
}
public class MyClass
{
public MyCustomType {get; set;}
}
Besides - in what other way would you ever want to serialize a raw integer? If the integer represents something, then create a struct or class for that something.
See also: "ValueObject" (Domain Driven Design fundamental concept)
Based on your comment below, an alternate approach would be to forget about JsonConverters and simply expose a secondary property:
public class MyClass
{
[JsonIgnore]
public Int64 MyInt {get; set;}
[JsonProperty("MyInt")]
public Int64 MyEncryptedInt
{
get { return Encrypt(MyInt); }
set { MyInt = Decrypt(value); }
}
}
that is my very first question I ask on this site, so forgive me if I missed something.
I have some problems deserializing an complex object graph using JSON.NET. My class hierarchy is (simplified) as follows:
public abstract class BusinessObjectBase
{
protected BusinessObjectBase(SerializationContext context)
{
}
}
public class TestBusinessObject : BusinessObjectBase
{
protected TestBusinessObject(SerializationContext context)
: base(context)
{
}
public NestedObject InnerObject { get; set; }
}
public class NestedObject : BusinessObjectBase
{
protected NestedObject(SerializationContext context)
: base(context)
{
}
}
The classes don't have a default ctor, but a dedicated custom deserialization ctor (beside other public ctor with parameters) as shown in the example. To create an instance I have written an custom creation converter like this:
internal class BusinessObjectCreationConverter : CustomCreationConverter<BusinessObjectBase>
{
public override bool CanConvert(Type objectType)
{
return typeof(BusinessObjectBase).IsAssignableFrom(objectType) && !objectType.IsAbstract;
}
public override BusinessObjectBase Create(Type objectType)
{
var businessObject = objectType.CreateUsingDesrializationConstructor<BusinessObjectBase>();
return businessObject;
}
}
The CreateUsingDesrializationConstructor() extension method looks for the special deserialization ctor and creates an instance using the ctor.
I added to the converter to my JSON.NET serializer instance:
public class NewtonsoftJsonSerializer : ISerializer
{
public NewtonsoftJsonSerializer()
: this(new JsonSerializer
{
TypeNameHandling = TypeNameHandling.Auto,
ObjectCreationHandling = ObjectCreationHandling.Replace,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
DefaultValueHandling = DefaultValueHandling.Ignore,
ContractResolver = new KsJsonContractResolver()
})
{
this.serializer.Converters.Add(new BusinessObjectCreationConverter());
}
public T Deserialize<T>(Stream stream)
{
T result;
using (var streamReader = new StreamReader(stream, Encoding.UTF8, true, BufferSize, true))
using (var jsonReader = new JsonTextReader(streamReader))
{
result = this.serializer.Deserialize<T>(jsonReader);
}
return result;
}
}
When I deserialize an TestBusinessObject I can see from the debugger that the converter is ask for every type whether he is able to create an instance: TestBusinessObject, NestedObject and many other types. But my converter is only requested to create a new TestBusinessObject instance, he is NOT requested to create the nested NestedObject instance what I have expected and what I badly need, since there is some wired logic in the deserialization ctor.
What I'm doing wrong here, how to tell the JsonSerializer to use the converter for every object not even for the root (top level) object?
EDIT:
Thinks become more even more complicated when a BusinessObjectBase instance is contained in an object that type I don't know. In this case I also want that the converter is called.
Thanks in advance,
Carsten
Try to give [DataContract] attributes to your classes that you want to serialize or deserialize and also make sure whatever data you have in these classes they are also having this attribute. for e.g.
[DataContract]
public class TestBusinessObject : BusinessObjectBase
{
protected TestBusinessObject(SerializationContext context)
: base(context)
{{
}
public NestedObject InnerObject { get; set; }
}
I've looked at various questions but I am unsure of how to implement this.
I have a custom struct, which currently has no public properties on it. When it is returned via WebApi (not doing any fancy serialization, just returning the custom struct itself), it is returned as an object {}.
public struct CustomStruct
{
private string myProperty;
...
public override string ToString()
{
return this.myProperty;
}
...
}
The custom struct itself is the type of a property on a parent class, which serializes to:
{ "MyProp1":"value1","MyProp2":"value2","MyCustomStruct":{} }
When I override ToString() on the custom struct I want to output one of the private properties. Can I achieve a similar behaviour when returning the object to JavaScript-land, as a JSON object?
E.g. my private property is a string, called "myProperty", set to "test".
If I added a public property called "MyProperty", I'd get the following output:
{ "MyProp1":"value1","MyProp2":"value2","MyCustomStruct":{ "MyProperty":"test" } }
When what I really want is:
{ "MyProp1":"value1","MyProp2":"value2","MyCustomStruct":"test" }
Hope this makes sense.
Here are the related questions that haven't really helped me much. Would like to avoid using JSON.NET if possible but will go for that if it is the only way:
JSON.Net Struct Serialization Discrepancy
C# custom json serialization
JSON.NET with Custom Serializer to a custom object
JSON serialization of enum as string
JavaScriptSerializer.Deserialize - how to change field names
I faced the same challenge. I solved it by writing a custom JsonConverter that uses the objects ToString method when converting:
public class ToStringConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
And then using it like this as JsonConverter attribute:
[JsonConverter(typeof(ToStringConverter))]
public AnotherObjectWithToString MyObject { get; set; }
I should note that this can only be used for serialization as deserialization would require the ToString result to converted back into a object and that will vary by type.
What I have done for now, is add a second property on the parent CustomClass class...
public string MyCustomStructValue { get { return MyCustomStruct.ToString(); } }
then add the [IgnoreDataMember] attribute to the original property...
[IgnoreDataMember]
public CustomStruct MyCustomStruct { get; set; }
which works fine with the following action:
public IEnumerable<CustomClass> Get()
{
return GetResults();
}
I am trying to set up a reader that will take in JSON objects from various websites (think information scraping) and translate them into C# objects. I am currently using JSON.NET for the deserialization process. The problem I am running into is that it does not know how to handle interface-level properties in a class. So something of the nature:
public IThingy Thing
Will produce the error:
Could not create an instance of type IThingy. Type is an interface or abstract class and cannot be instantiated.
It is relatively important to have it be an IThingy as opposed to a Thingy since the code I am working on is considered sensitive and unit testing is highly important. Mocking of objects for atomic test scripts is not possible with fully-fledged objects like Thingy. They must be an interface.
I've been poring over JSON.NET's documentation for a while now, and the questions I could find on this site related to this are all from over a year ago. Any help?
Also, if it matters, my app is written in .NET 4.0.
#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
(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);
}
}
Use this class, for mapping abstract type, to real type:
public class AbstractConverter<TReal, TAbstract>
: JsonConverter where TReal : TAbstract
{
public override Boolean CanConvert(Type objectType)
=> objectType == typeof(TAbstract);
public override Object ReadJson(JsonReader reader, Type type, Object value, JsonSerializer jser)
=> jser.Deserialize<TReal>(reader);
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer jser)
=> jser.Serialize(writer, value);
}
and when deserialize:
var settings = new JsonSerializerSettings
{
Converters = {
new AbstractConverter<Thing, IThingy>(),
new AbstractConverter<Thing2, IThingy2>()
},
};
JsonConvert.DeserializeObject(json, type, settings);
To enable deserialization of multiple implementations of interfaces, you can use JsonConverter, but not through an attribute:
Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Converters.Add(new DTOJsonConverter());
Interfaces.IEntity entity = serializer.Deserialize(jsonReader);
DTOJsonConverter maps each interface with a concrete implementation:
class DTOJsonConverter : Newtonsoft.Json.JsonConverter
{
private static readonly string ISCALAR_FULLNAME = typeof(Interfaces.IScalar).FullName;
private static readonly string IENTITY_FULLNAME = typeof(Interfaces.IEntity).FullName;
public override bool CanConvert(Type objectType)
{
if (objectType.FullName == ISCALAR_FULLNAME
|| objectType.FullName == IENTITY_FULLNAME)
{
return true;
}
return false;
}
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
if (objectType.FullName == ISCALAR_FULLNAME)
return serializer.Deserialize(reader, typeof(DTO.ClientScalar));
else if (objectType.FullName == IENTITY_FULLNAME)
return serializer.Deserialize(reader, typeof(DTO.ClientEntity));
throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
DTOJsonConverter is required only for the deserializer. The serialization process is unchanged. The Json object do not need to embed concrete types names.
This SO post offers the same solution one step further with a generic JsonConverter.
Nicholas Westby provided a great solution in a awesome article.
If you want Deserializing JSON to one of many possible classes that implement an interface like that:
public class Person
{
public IProfession Profession { get; set; }
}
public interface IProfession
{
string JobTitle { get; }
}
public class Programming : IProfession
{
public string JobTitle => "Software Developer";
public string FavoriteLanguage { get; set; }
}
public class Writing : IProfession
{
public string JobTitle => "Copywriter";
public string FavoriteWord { get; set; }
}
public class Samples
{
public static Person GetProgrammer()
{
return new Person()
{
Profession = new Programming()
{
FavoriteLanguage = "C#"
}
};
}
}
You can use a custom JSON converter:
public class ProfessionConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IProfession);
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var profession = default(IProfession);
switch (jsonObject["JobTitle"].Value())
{
case "Software Developer":
profession = new Programming();
break;
case "Copywriter":
profession = new Writing();
break;
}
serializer.Populate(jsonObject.CreateReader(), profession);
return profession;
}
}
And you will need to decorate the "Profession" property with a JsonConverter attribute to let it know to use your custom converter:
public class Person
{
[JsonConverter(typeof(ProfessionConverter))]
public IProfession Profession { get; set; }
}
And then, you can cast your class with an Interface:
Person person = JsonConvert.DeserializeObject<Person>(jsonString);
I found this useful. You might too.
Example Usage
public class Parent
{
[JsonConverter(typeof(InterfaceConverter<IChildModel, ChildModel>))]
IChildModel Child { get; set; }
}
Custom Creation Converter
public class InterfaceConverter<TInterface, TConcrete> : CustomCreationConverter<TInterface>
where TConcrete : TInterface, new()
{
public override TInterface Create(Type objectType)
{
return new TConcrete();
}
}
Json.NET documentation
Two things you might try:
Implement a try/parse model:
public class Organisation {
public string Name { get; set; }
[JsonConverter(typeof(RichDudeConverter))]
public IPerson Owner { get; set; }
}
public interface IPerson {
string Name { get; set; }
}
public class Tycoon : IPerson {
public string Name { get; set; }
}
public class Magnate : IPerson {
public string Name { get; set; }
public string IndustryName { get; set; }
}
public class Heir: IPerson {
public string Name { get; set; }
public IPerson Benefactor { get; set; }
}
public class RichDudeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IPerson));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// pseudo-code
object richDude = serializer.Deserialize<Heir>(reader);
if (richDude == null)
{
richDude = serializer.Deserialize<Magnate>(reader);
}
if (richDude == null)
{
richDude = serializer.Deserialize<Tycoon>(reader);
}
return richDude;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Left as an exercise to the reader :)
throw new NotImplementedException();
}
}
Or, if you can do so in your object model, implement a concrete base class between IPerson and your leaf objects, and deserialize to it.
The first can potentially fail at runtime, the second requires changes to your object model and homogenizes the output to the lowest common denominator.
For those that might be curious about the ConcreteListTypeConverter that was referenced by Oliver, here is my attempt:
public class ConcreteListTypeConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var res = serializer.Deserialize<List<TImplementation>>(reader);
return res.ConvertAll(x => (TInterface) x);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
No object will ever be an IThingy as interfaces are all abstract by definition.
The object you have that was first serialized was of some concrete type, implementing the abstract interface. You need to have this same concrete class revive the serialized data.
The resulting object will then be of some type that implements the abstract interface you are looking for.
From the documentation it follows that you can use
(Thingy)JsonConvert.DeserializeObject(jsonString, typeof(Thingy));
when deserializing to inform JSON.NET about the concrete type.
For what it's worth, I ended up having to handle this myself for the most part. Each object has a Deserialize(string jsonStream) method. A few snippets of it:
JObject parsedJson = this.ParseJson(jsonStream);
object thingyObjectJson = (object)parsedJson["thing"];
this.Thing = new Thingy(Convert.ToString(thingyObjectJson));
In this case, new Thingy(string) is a constructor that will call the Deserialize(string jsonStream) method of the appropriate concrete type. This scheme will continue to go downward and downward until you get to the base points that json.NET can just handle.
this.Name = (string)parsedJson["name"];
this.CreatedTime = DateTime.Parse((string)parsedJson["created_time"]);
So on and so forth. This setup allowed me to give json.NET setups it can handle without having to refactor a large part of the library itself or using unwieldy try/parse models that would have bogged down our entire library due to the number of objects involved. It also means that I can effectively handle any json changes on a specific object, and I do not need to worry about everything that object touches. It's by no means the ideal solution, but it works quite well from our unit and integration testing.
Suppose an autofac setting like the following:
public class AutofacContractResolver : DefaultContractResolver
{
private readonly IContainer _container;
public AutofacContractResolver(IContainer container)
{
_container = container;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
// use Autofac to create types that have been registered with it
if (_container.IsRegistered(objectType))
{
contract.DefaultCreator = () => _container.Resolve(objectType);
}
return contract;
}
}
Then, suppose your class is like this:
public class TaskController
{
private readonly ITaskRepository _repository;
private readonly ILogger _logger;
public TaskController(ITaskRepository repository, ILogger logger)
{
_repository = repository;
_logger = logger;
}
public ITaskRepository Repository
{
get { return _repository; }
}
public ILogger Logger
{
get { return _logger; }
}
}
Therefore, the usage of the resolver in deserialization could be like:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<TaskRepository>().As<ITaskRepository>();
builder.RegisterType<TaskController>();
builder.Register(c => new LogService(new DateTime(2000, 12, 12))).As<ILogger>();
IContainer container = builder.Build();
AutofacContractResolver contractResolver = new AutofacContractResolver(container);
string json = #"{
'Logger': {
'Level':'Debug'
}
}";
// ITaskRespository and ILogger constructor parameters are injected by Autofac
TaskController controller = JsonConvert.DeserializeObject<TaskController>(json, new JsonSerializerSettings
{
ContractResolver = contractResolver
});
Console.WriteLine(controller.Repository.GetType().Name);
You can see more details in http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm
My solution to this one, which I like because it is nicely general, is as follows:
/// <summary>
/// Automagically convert known interfaces to (specific) concrete classes on deserialisation
/// </summary>
public class WithMocksJsonConverter : JsonConverter
{
/// <summary>
/// The interfaces I know how to instantiate mapped to the classes with which I shall instantiate them, as a Dictionary.
/// </summary>
private readonly Dictionary<Type,Type> conversions = new Dictionary<Type,Type>() {
{ typeof(IOne), typeof(MockOne) },
{ typeof(ITwo), typeof(MockTwo) },
{ typeof(IThree), typeof(MockThree) },
{ typeof(IFour), typeof(MockFour) }
};
/// <summary>
/// Can I convert an object of this type?
/// </summary>
/// <param name="objectType">The type under consideration</param>
/// <returns>True if I can convert the type under consideration, else false.</returns>
public override bool CanConvert(Type objectType)
{
return conversions.Keys.Contains(objectType);
}
/// <summary>
/// Attempt to read an object of the specified type from this reader.
/// </summary>
/// <param name="reader">The reader from which I read.</param>
/// <param name="objectType">The type of object I'm trying to read, anticipated to be one I can convert.</param>
/// <param name="existingValue">The existing value of the object being read.</param>
/// <param name="serializer">The serializer invoking this request.</param>
/// <returns>An object of the type into which I convert the specified objectType.</returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
try
{
return serializer.Deserialize(reader, this.conversions[objectType]);
}
catch (Exception)
{
throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
}
}
/// <summary>
/// Not yet implemented.
/// </summary>
/// <param name="writer">The writer to which I would write.</param>
/// <param name="value">The value I am attempting to write.</param>
/// <param name="serializer">the serializer invoking this request.</param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
You could obviously and trivially convert it into an even more general converter by adding a constructor which took an argument of type Dictionary<Type,Type> with which to instantiate the conversions instance variable.
Several years on and I had a similar issue. In my case there were heavily nested interfaces and a preference for generating the concrete classes at runtime so that It would work with a generic class.
I decided to create a proxy class at run time that wraps the object returned by Newtonsoft.
The advantage of this approach is that it does not require a concrete implementation of the class and can handle any depth of nested interfaces automatically. You can see more about it on my blog.
using Castle.DynamicProxy;
using Newtonsoft.Json.Linq;
using System;
using System.Reflection;
namespace LL.Utilities.Std.Json
{
public static class JObjectExtension
{
private static ProxyGenerator _generator = new ProxyGenerator();
public static dynamic toProxy(this JObject targetObject, Type interfaceType)
{
return _generator.CreateInterfaceProxyWithoutTarget(interfaceType, new JObjectInterceptor(targetObject));
}
public static InterfaceType toProxy<InterfaceType>(this JObject targetObject)
{
return toProxy(targetObject, typeof(InterfaceType));
}
}
[Serializable]
public class JObjectInterceptor : IInterceptor
{
private JObject _target;
public JObjectInterceptor(JObject target)
{
_target = target;
}
public void Intercept(IInvocation invocation)
{
var methodName = invocation.Method.Name;
if(invocation.Method.IsSpecialName && methodName.StartsWith("get_"))
{
var returnType = invocation.Method.ReturnType;
methodName = methodName.Substring(4);
if (_target == null || _target[methodName] == null)
{
if (returnType.GetTypeInfo().IsPrimitive || returnType.Equals(typeof(string)))
{
invocation.ReturnValue = null;
return;
}
}
if (returnType.GetTypeInfo().IsPrimitive || returnType.Equals(typeof(string)))
{
invocation.ReturnValue = _target[methodName].ToObject(returnType);
}
else
{
invocation.ReturnValue = ((JObject)_target[methodName]).toProxy(returnType);
}
}
else
{
throw new NotImplementedException("Only get accessors are implemented in proxy");
}
}
}
}
Usage:
var jObj = JObject.Parse(input);
InterfaceType proxyObject = jObj.toProxy<InterfaceType>();
Use this JsonKnownTypes, it's very similar way to use, it just add discriminator to json:
[JsonConverter(typeof(JsonKnownTypeConverter<Interface1>))]
[JsonKnownType(typeof(MyClass), "myClass")]
public interface Interface1
{ }
public class MyClass : Interface1
{
public string Something;
}
Now when you serialize object in json will be add "$type" with "myClass" value and it will be use for deserialize
Json:
{"Something":"something", "$type":"derived"}
My solution was added the interface elements in the constructor.
public class Customer: ICustomer{
public Customer(Details details){
Details = details;
}
[JsonProperty("Details",NullValueHnadling = NullValueHandling.Ignore)]
public IDetails Details {get; set;}
}
You can also use custom TextInputFormatter, no external libraries needed, also helps you gain insight on how you can handle (de)serialization of any type of data.
public class MyInputTypeFormatter : TextInputFormatter
{
public MyInputTypeFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
SupportedEncodings.Add(Encoding.UTF8);
}
protected override bool CanReadType(Type type)
{
return type == typeof(MyClass);
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<ImageTypeConverter>>();
using var reader = new StreamReader(httpContext.Request.Body, encoding);
{
var data = await reader.ReadToEndAsync();
if (data.Contains("Hello"))
{
var myClass= new MyClass(data);
return await InputFormatterResult.SuccessAsync(myClass);
}
else
{
return await InputFormatterResult.FailureAsync();
}
}
}
}
Then, simply add this input formatter to the list of input formatters with
services.AddControllers(options=> {
options.InputFormatters.Insert(0, new MyInputFormatter());
});
0 here means this is the first input formatter invoked when model binding.
It seems like a lot of work but most of it is just boilerplate.
I will explain how this works,
You have an action method/ route which has a parameter of MyClass type. When a request comes to it, your input formatter's CanReadType is invoked and it returns true meaning it will handle the deserialization.Then the ReadRequestBodyAsync method is invoked and the request data is given to it.
You can do whatever you want with the data and return an object of type MyClass if your deserialization succeeds. Else you just return a failure.
In the deserialization you can use
using (JsonDocument document = JsonDocument.Parse(jsonString))
{
JsonElement root = document.RootElement;
// ...
}
You can traverse elements as the input is parsed into a json object and then held into a DOM. Then you can see what they contain and manually create classes with their data and convert your input-as-interfaces into classes.
Note: JsonDocument was introduced in .Net 3.1
You can check out how to use it here
More about how to use TextInputFormatter and TextOutputFormatter
The benefit of using a custom input formatter is that it provides a central class for handling your custom classes which may use multiple interfaces. It also gives you fine control over handling the input data.