I have a class decorated with JsonConverter attribute to use my custom converter. The aim of the custom converter is to serialize CustomProperty
using some custom logic. Instead of writing code to serialize all the primitive properties, I decided to use JObject.FromObject
to automatically serialize the properties and would later do something like o.Remove("CustomProperty")
and then add the custom serialized member to o
.
But since the class is decorated with JsonConverter
attribute, JObject.FromObject
again calls my ClassAJsonConverter
which leads to infinte recursive call. At the point of calling JObject.FromObject
is it possible to specifically tell the json to use it's default converter instead of my custom one.
[Newtonsoft.Json.JsonConverter(typeof(ClassAJsonConverter))]
public class ClassA
{
public string A {get; set;}
public int B {get; set;}
.
//20 some properties
.
public CustomProp CustomProperty {get; set;}
}
public class ClassAJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ClassA);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
.
var o = JObject.FromObject(value); //Here infinite recurrence occur
.
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
.
.
.
}
}
Note: I came across Recursively call JsonSerializer in a JsonConverter but was not able to implement it. Also I would not like to add dependency to AutoMapper just for this one use. Since the question was more than a year old, has anyone found a better way to do it?
If you've decorated your class with a [JsonConverter]
attribute, then all instances of the serializer will know about it. Therefore if you use JObject.FromObject
within the converter you will get into an infinite recursive loop, even if you try to pass it a new serializer instance.
There are two ways around the problem:
JObject.FromObject
, or[JsonConverter]
attribute from the class declaration, and instead pass an instance of the converter to the serializer. This approach relies on the correct implementation of CanConvert
(as shown in your question) so that the converter is applied only to the intended class.For example:
string json = JsonConvert.SerializeObject(classA, new ClassAJsonConverter());
If the serialization of your CustomProperty
does not depend on the other members of ClassA
, then another alternative is to create a custom converter specifically for the CustomProp
class instead of ClassA
. Then your converter doesn't have to do tricks to worry about other properties; it just has to worry about CustomProp
itself.
Another possible solution
I found a solution that might work for you, but it feels a little hacky. The idea is to create a new JsonSerializer
instance inside the JsonConverter
, and then use a special ContractResolver
on that serializer which disavows knowledge of the current converter when asked to resolve it. This will allow you to use JObject.FromObject
inside the converter without getting into a recursive loop, even while you have the [JsonConverter]
attribute applied to your class. The downside to this approach is that any other settings that you may have applied to the outer serializer will not be automatically carried to the inner serializer, so you will need to manually copy those settings if you need to keep them.
Here is the code for the resolver:
class JsonConverterExclusionResolver<T> : DefaultContractResolver
{
protected override JsonConverter ResolveContractConverter(Type objectType)
{
JsonConverter conv = base.ResolveContractConverter(objectType);
if (conv != null && conv.GetType() == typeof(T))
{
// if something asks for the converter we're excluding,
// we never heard of it
return null;
}
return conv;
}
}
With this resolver in place, you would need to modify your ClassAJsonConverter
to use it like this:
public class ClassAJsonConverter : JsonConverter
{
private IContractResolver exclusionResolver =
new JsonConverterExclusionResolver<ClassAJsonConverter>();
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ClassA);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JsonSerializer innerSerializer = new JsonSerializer();
innerSerializer.ContractResolver = exclusionResolver;
// (copy other settings from the outer serializer if needed)
var o = JObject.FromObject(value, innerSerializer);
// ...do your custom stuff here...
o.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With