The System.Text.Json serializer that was introduced with .NET Core 3.0 gets new features with every new .NET version. With .NET 7, features such as type hieararchies, contract customization, and source generator features have been added. This article shows using the JSON serializer with a hierarchy, and using a source generator.
The model that’s defined defines the types
Game class specifies a few simple properties as well as a property of type
ICollection that specifies a relationship. Using the primary constructor syntax, the C# compiler creates init-only properties with C# class records and a constructor that initializes the properties. The
Moves property of type
ICollection is initialized with a
List. In this scneario, the collection contains types deriving from the abstract
The hierarchy is created with the
Move type. The generic version
Move derives from the non-generic abstract
Move class. The
Move class specifies a property of type
ICollection that has a collection of fields of a single game move. The type
TField is a generic parameter which allows using different game types. For example, a move can consist of a list of different colors, or with another game type, a list of different shapes and colors. The values of the
Move class as well as the generic classes deriving from it should be written to the serialized JSON content.
The types that will be used for the generic type parameter
TField are the records
ColorField wraps a color specified by a string,
ShapeAndColorField wraps two strings specifying a shape and a color.
To serialize the derived classes to JSON, the deserializer needs to know the types to create. To allow this, the
JsonPolymorphic attribute is used to specify a key that is stored on serialization with the JSON content. With the sample code, the name
$discriminator is used for the key. The values for this discriminator are specified with the
JsonDerivedType is applied to the base class
Move to specify what derived types should be serialized, and what discriminator value should be used with serialization. Using deserialization the value is used to create an instance of the mapped class.
> Because the
Moves property of the
Game class is needed with deserialization, the
get accessor is not enough. In the code sample, a
private init accessor together with the
JsonInclude attribute is used.
The model types are in place, next let’s create and serialize instances with
JsonSerializer.Serialize. The serialization is customized passing
JsonSerializerOptions. With the sample code, the naming policy is set to camel case which is the same configuration used from a Web API. For a nicer output on the console, write indented is configured. Invoking
JsonSerializer.Deserialize, .NET objects are created from JSON:
Checking the output information, you can see the
$discriminator and the values used specified from the attribute:
JSON Source Generator
To reduce reflection and instead generate source code at compile time, the JSON source generator can be used. This generator is activated by creatign a partial class that derives from the base class
JsonSerializerContext, and annoatating it with the
GamesContext class in the following code snippet uses the same settings as the
JsonSerializerOptions with the previous sample.
Contrary to the previous sample using properties with
init accessors, this is not possible with the .NET 7 JSON source generator. The .NET 7 source generator doesn’t support
init accessors and
required modifiers. Using preview 1 of .NET 8, this is already possible. See a link below for upcoming
System.Text.Json updates with .NET 8.
To use the generated source code, the context needs to be passed as an argument of the
Deserialize methods as shown in the next code snippet. The
GamesContext.Default property is created from the source generator.
To check the generated code, you can open the Visual Studio Solution Explorer, open Analyzers bewlow Dependencies, and expand the System.Text.Json.SourceGeneration. Here you find how all the different attributes apply to source code.
While the first version of System.Text.Json was very fast, it was limited with its features. Every .NET version since then, the JSON serializer has been enhanced. With .NET 7 it supports a hierarchy of classes by applying simple attributes. This serializer reduces memory needs and enhances performance by using the
Span type. With the source generator, more performance imporovements are possible, and the way gets opened for using AOT compilation with .NET 8.
Enjoy learning and programming!
If you like this article, please buy Christian a cup of coffee by going here. Thanks!
More information about programming EF Core is available in my book and my workshops.
Read more about files, streams, and JSON serialization in my book Professional C# and .NET – 2021 Edition
See Chapter 18, "Files and Streams".
System.Text.Json Work planned for .NET 8
The complete source code of this sample is available on Professional C# source code – see the folders 5_More/FilesAndStreams/JsonInheritance and JsonInheritanceWithSourceGenerator
Thank you for your download Get a free download for crediting the author! [Photo Refreshing Mountain Stream 3147742 © Psychocy] | Dreamstime.com]()
3 thoughts on “System.Text.Json Serializing Hierarchical Data”