JSON.net ContractResolver vs. JsonConverter - json.net

I've been working with JSON.net for a while. I have written both custom converters and custom contract resolvers (generally from modifying examples on S.O. and the Newtonsoft website), and they work fine.
The challenge is, other than examples, I see little explanation as to when I should use one or the other (or both) for processing. Through my own experience, I've basically determined that contract resolvers are simpler, so if I can do what I need with them, I go that way; otherwise, I use custom JsonConverters. But, I further know both are sometimes used together, so the concepts get further opaque.
Questions:
Is there a source that distinguishes when to user one vs. the other? I find the Newtonsoft documentation unclear as to how the two are differentiated or when to use one or the other.
What is the pipeline of ordering between the two?

Great question. I haven't seen a clear piece of documentation that says when you should prefer to write a custom ContractResolver or a custom JsonConverter to solve a particular type of problem. They really do different things, but there is some overlap between what kinds of problems can be solved by each. I've written a fair number of each while answering questions on StackOverflow, so the picture has become a little more clear to me over time. Below is my take on it.
ContractResolver
A contract resolver is always used by Json.Net, and governs serialization / deserialization behavior at a broad level. If there is not a custom resolver provided in the settings, then the DefaultContractResolver is used. The resolver is responsible for determining:
what contract each type has (i.e. is it a primitive, array/list, dictionary, dynamic, JObject, plain old object, etc.);
what properties are on the type (if any) and what are their names, types and accessibility;
what attributes have been applied (e.g. [JsonProperty], [JsonIgnore], [JsonConverter], etc.), and
how those attributes should affect the (de)serialization of each property (or class).
Generally speaking, if you want to customize some aspect of serialization or deserialization across a wide range of classes, you will probably need to use a ContractResolver to do it. Here are some examples of things you can customize using a ContractResolver:
Change the contract used for a type
Serialize all Dictionaries as an Array of Key/Value Pairs
Serialize ListItems as a regular object instead of string
Change the casing of property names when serializing
Use camel case for all property names
Camel case all property names except dictionaries
Programmatically apply attributes to properties without having to modify the classes (particularly useful if you don't control the source of said classes)
Globally use a JsonConverter on a class without the attribute
Remap properties to different names defined at runtime
Allow deserializing to public properties with non-public setters
Programmatically unapply (ignore) attributes that are applied to certain classes
Optionally turn off the JsonIgnore attribute at runtime
Make properties which are marked as required (for SOAP) not required for JSON
Conditionally serialize properties
Ignore read-only properties across all classes
Skip serializing properties that throw exceptions
Introduce custom attributes and apply some custom behavior based on those attributes
Encrypt specially marked string properties in any class
Selectively escape HTML in strings during deserialization
JsonConverter
In contrast to a ContractResolver, the focus of a JsonConverter is more narrow: it is really intended to handle serialization or deserialization for a single type or a small subset of related types. Also, it works at a lower level than a resolver does. When a converter is given responsibility for a type, it has complete control over how the JSON is read or written for that type: it directly uses JsonReader and JsonWriter classes to do its job. In other words, it can change the shape of the JSON for that type. At the same time, a converter is decoupled from the "big picture" and does not have access to contextual information such as the parent of the object being (de)serialized or the property attributes that were used with it. Here are some examples of problems you can solve with a JsonConverter:
Handle object instantiation issues on deserialization
Deserialize to an interface, using information in the JSON to decide which concrete class to instantiate
Deserialize JSON that is sometimes a single object and sometimes an array of objects
Deserialize JSON that can either be an array or a nested array
Skip unwanted items when deserializing from an array of mixed types
Deserialize to an object that lacks a default constructor
Change how values are formatted or interpretted
Serialize decimal values as localized strings
Convert decimal.MinValue to an empty string and back (for use with a legacy system)
Serialize dates with multiple different formats
Ignore UTC offsets when deserializing dates
Make Json.Net call ToString() when serializing a type
Translate between differing JSON and object structures
Deserialize a nested array of mixed values into a list of items
Deserialize an array of objects with varying names
Serialize/deserialize a custom dictionary with complex keys
Serialize a custom IEnumerable collection as a dictionary
Flatten a nested JSON structure into a simpler object structure
Expand a simple object structure into a more complicated JSON structure
Serialize a list of objects as a list of IDs only
Deserialize a JSON list of objects containing GUIDs to a list of GUIDs
Work around issues (de)serializing specific .NET types
Serializing System.Net.IPAddress throws an exception
Problems deserializing Microsoft.Xna.Framework.Rectangle

Related

System.Text.Json serialization not using derived classes

I have an abstract class named Extension which has several derived classes such as DerivedExtensionA, DerivedExtensionB, etc.
Now I have a list defined as List<Extension> which contains the derived classes instances.
Now if I serialize the above list, it only serializes the base class properties that are in Extension since the list has the base class Extension type. If I define the list as List<DerivedExtensionA> and then put only instances of DerivedExtensionA in it, then they are serialized fine. But my code is generic which is supposed to accept all types of Extensions, so this isn't a workable solution for me.
So question is ..
How do I keep the list defined as List<Extension> and still be able to fully serialize the contained derived class instances that contain ALL their properties ?
Here is a fiddle showing this behavior: https://dotnetfiddle.net/22mbwb
EDIT: Corrected the fiddle URL
From How to serialize properties of derived classes with System.Text.Json
Serialization of a polymorphic type hierarchy is not supported.
In your fiddle you can use an array of objects:
string allExtensionsSerialized =
JsonSerializer.Serialize((object[])allExtensions.ToArray());
This is the hack I used recently:
public record MyType(
// This nonsense is here because System.Text.Json does not support normal polymorphic serialisation
[property: JsonIgnore] List<X> Messages))
{
// This nonsense is here because System.Text.Json does not support normal polymorphic serialisation
[JsonPropertyName("Messages")]
public object[] MessagesTrick => Messages.ToArray();
For deserialisation, I decided used JsonDocument.Parse inside a dedicated FromJson(string json) method. This works OK, for me, in this specific case.
Actually I ended up changing the definition of the list from List<Extension> to List<object>, and the behavior was corrected. This might not be a workable solution for everyone reading this, but it's fine for me so that's why I'm adding my own answer.

Change default member serialization mode to MemberSerialization.Fields

I would like to change the default serialization mode in Json.NET from MemberSerialization.OptOut to MemberSerialization.Fields without needing to scatter [JsonObject(MemberSerialization.Fields)] attributes through every class in my code base.
My primary reasons for wanting this is to get my Json deserializer to not call constructors and to make sure that all fields, including private ones, are correctly serialized and deserialized.
An example of the behaviour I'm looking for would be the serialization / deserialization behaviour of the BinaryFormatter, except without needing to scatter [Serializable] attributes through the entire codebase.

Change default GUID serialization format

By default, the JSON and XML serializers in Web API 2 just call ToString() to serialize out a Guid, which means it includes dashes: fd190000-5532-8477-e053-9a16ce0af828. I'd really like to change the default serialization to the format returned by ToString("N"), which does not include dashes: fd19000055328477e0539a16ce0af828.
I found this article on creating a JsonConverter and overriding the WriteJson() method to use ToString("N"). That worked fine for JSON, but I haven't found anything similar for XML serialization.
Is there a way to implement this only once, regardless of how many MediaTypeFormatters are present? If not, how can I override XML serialization as well?

ASP.NET json serializer only includes __type for top level class

I'm using [webmethod] attribute in an aspx page to expose a complex data structure(autogenerated from xml->xsd->xsd.exe) to the browser.
After I get the data on the client with jquery, only the top level object includes the "__type" attribute, so I cannot determine the class of any of the nested objects(attributes, array content etc).
Is there a way to make the asp.net serializer include the __type attribute recursively when serializing an object?
I encountered a similar situation with another JSON serializer, the one from ServiceStack.Text. They will put in the __type property if an object is declared as an interface or an abstract class, perhaps the vanilla .NET serializer will do so as well? If that's the case, is there any way for you to declare your object[] array as an array of interfaces? Even something as trivial as cerating an IEmpty with no methods will do the trick.

What are the downsides of "typing" your control state?

I am relatively new to Web Parts and Web Forms (I have only worked a lot with the MVC framework).
I am planning to store some data in the control state. All the examples I can find put an object[] array in the control state and the base control state on the 0 index.
I don't really like putting everything in an object[], so I wanted to create an extra class for my web part with typed properties: e.g. MyWebPartControlState. I will store the base control state in a property BaseControlState of type object.
I was wondering if this could cause any problems or if there are any other reasons why this might not be a good idea. I am wondering because it feels logical to me, but I cannot find any examples of control state where they don't put everything in the control state directly or in a object[].
Thanks in advance.
The control state is persisted in the same field as view state and follows the same rules for serialization. All the samples you found use an object array because that's one of the types the optimized state serializer in ASP.NET understands and for which is able to optimize serialization.
If you use a custom object the serializer won't use the optimizations and instead will serialize your object using the BinaryFormatter which will lead to bloated control state. If you want to have a strongly typed view of your state you should implement IStateManager on your custom class so that it encapsulates the transformation of itself from and to simple objects that the serializer understands.
If I recall correctly the serializer can efficiently serialize the following types:
Primitive types (int, long, etc);
DateTime;
string;
Boxed values of supported value types;
Object arrays containing instances of supported types;
Pair and Triplet objects containing instances of supported types.
I wrote a short blogpost illustrating the differences in size resulting from a simple custom class being serialized with BinaryFormatter versus implementing IStateManager and decomposing to simple types that the serializer can optimize. You can find it at:
ASP.NET ViewState Tips and Tricks #2

Resources