I want to modify my json.NET serializer to add the $type property only to the objects which implements a given interface but not to any property or nested objects.
<
If you require the "$type"
property on your root object and are OK with it appearing on nested polymorphic objects and arrays if required, use the following overload along with TypeNameHandling.Auto
: JsonConvert.SerializeObject(Object, Type, JsonSerializerSettings).
From the docs:
public static string SerializeObject( Object value, Type type, JsonSerializerSettings settings )
type Type: System.Type The type of the value being serialized. This parameter is used when TypeNameHandling is Auto to write out the type name if the type of the value does not match. Specifing the type is optional.
I.e., do:
var serializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
var event1Serialized = JsonConvert.SerializeObject(event1, typeof(IEvent), serializerSettings);
If you require "$type"
on the root object and will not accept it on nested polymorphic objects and arrays even if otherwise required, you will need to use TypeNameHandling.All
along with a custom contract resolver that sets JsonContainerContract.ItemTypeNameHandling = TypeNameHandling.None:
public class SuppressItemTypeNameContractResolver : DefaultContractResolver
{
// As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
// http://www.newtonsoft.com/json/help/html/ContractResolver.htm
// http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
// "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
static SuppressItemTypeNameContractResolver instance;
// Using a static constructor enables fairly lazy initialization. http://csharpindepth.com/Articles/General/Singleton.aspx
static SuppressItemTypeNameContractResolver() { instance = new SuppressItemTypeNameContractResolver(); }
public static SuppressItemTypeNameContractResolver Instance { get { return instance; } }
protected SuppressItemTypeNameContractResolver() : base() { }
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
var containerContract = contract as JsonContainerContract;
if (containerContract != null)
{
if (containerContract.ItemTypeNameHandling == null)
containerContract.ItemTypeNameHandling = TypeNameHandling.None;
}
return contract;
}
}
Then use it like:
var serializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All,
ContractResolver = SuppressItemTypeNameContractResolver.Instance,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
var event1Serialized = JsonConvert.SerializeObject(event1, serializerSettings);
Finally, do take note of this caution from the Newtonsoft docs:
TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.
For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json, How to configure Json.NET to create a vulnerable web API, and Alvaro Muñoz & Oleksandr Mirosh's blackhat paper https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf