JSON, as a data format, uses implicit types, which saves a lot of space and is perfect for when there is a strict predefined contract. But what happens when there is a property that can take values of multiple different types that don’t necessarily derive from one single base? For example:
For this entity, the
ObjectProperty is implemented in our corresponding C# class as a property of type
object, meaning that it can take value of any type.
This implementation will not present any immediate difficulties, because JSON.net is smart enough to figure out the primitive types by their value and it will properly assign
3.14 as double, etc.
Problems occur, however, when you start dealing with complex objects, even something relatively simple as an array:
Despite it being obviously just a string array, JSON.net will deserialize it as a
JArray object, which we can’t deal with directly and ultimately don’t want in our model objects. Same thing will happen with any other non-primitive value, such as dictionaries, class instances, etc, in which case a regular
JToken object will be returned instead.
If you have control over the serializer that produces the given JSON content (internal data exchange, file formats, etc), the easiest way to deal with this is to set
TypeNameHandling = TypeNameHandling.Auto for the
JsonSerializationSettings instance responsible for serialization. This will make it use an internal JSON.net property that helps it identify the type of object it’s dealing with.
When set to
TypeNameHandling.Auto it will not generate this meta data for types it can (de)serialize implicitly, so the previous examples will still work the same. However, this changes the data format in a way, only understandable by JSON.net and it also has references to .NET assemblies, which is very undesired if you intend to achieve proper separation of logic and concern. It also only works if you have control over the actual serializer that produces the JSON, which rarely happens.
If you don’t have control over the incoming JSON (received from API, strict rules, etc) it’s possible to define a custom
ContractResolver on the receiving end that will help properly deal with the entities you expect.
ContractResolver works with generalized class definitions and not class instances, so it’s not very useful to implement logic, based on a specific state of an object. However, there are two delegates that actually work with an instance – the
In a default contract resolver, JSON.net uses naming conventions to locate and execute methods that check whether a property needs to be (de)serialized, for example –
ShouldSerializeMyInt that returns
bool will decide if
MyInt property needs to be serialized. However, when making a custom resolver, it is possible to override this logic and set these delegates to something else.
The delegate signature itself is just a method that takes the instance as parameter (of type
object) and returns a
bool value. It’s not intended, but is also possible to use this delegate to make changes to the property’s contract, changing its type based on the object instance.
This way, just before deserializing, an instance method called
GetObjectPropertyType will be executed, setting a new
PropertyType for the object's contract.
This method essentially abuses the
ShouldDeserialize delegates as handlers for events that occur before serialization and deserialization, which allows it to change the contract on the fly.