Extensions to System.Text.Json
Please participate to the discussion here
The main purpose of this library is to bring missing features to the official .Net namespace System.Text.Json
- .NET Standard 2.0
- .NET Core 3.1
- .NET 5
- .NET 6
- Extensible Polymorphism support based on discriminator conventions (example)
- Conditional Property Serialization support based on the existence of a method ShouldSerialize[PropertyName]() (example)
- Support for interfaces and abstract classes (example)
- Support for numeric, enum and custom dictionary keys (example)
- Support for non default constructors (example)
- Can ignore default values (example)
- Can require properties or fields with different policies (example)
- Object mapping to programmatically configure features in a non invasive way (example)
- Support for Writable JSON Document Object Model (cf. Spec) (example)
- Support for serialization callbacks (before/after serialization/deserialization) (example)
- Support for anonymous types (example)
- Support for DataContractAttribute and DataMemberAttribute (example)
- Extended support for structs (example)
- Support for Nullables (example)
- Support for collection interfaces: ISet<>, IList<>, ICollection<>, IEnumerable<>, IReadOnlyList<>, IReadOnlyCollection<>, IDictionary<>, IReadOnlyDictionary<> (example)
- Support for immutable collections: ImmutableArray<>, ImmutableList<>, ImmutableSortedSet<>, ImmutableHashSet<>, ImmutableDictionary<>, ImmutableSortedDictionary<> (example)
- Support for reference loop handling (cf. https://github.com/dotnet/corefx/issues/41002) (example)
- Support for deserializing into read-only properties (cf. https://github.com/dotnet/corefx/issues/40602) (example)
- Support for dynamics (example)
- Support for C# 8 nullable reference types
- Support for MissingMemberHandling (example)
- Support for different property requirement policy (example)
- Support for extended naming policies: Kebab case and Snake case (example1, example2)
- Support for duplicated property handling
https://www.nuget.org/packages/Dahomey.Json/
Install-Package Dahomey.Json
dotnet restore
dotnet pack -c Release
using Dahomey.Json;
using System.Text.Json;
JsonSerializerOptions options = new JsonSerializerOptions();
options.SetupExtensions();
You can also instantiate options and setup extensions in one line:
using Dahomey.Json;
using System.Text.Json;
JsonSerializerOptions options = new JsonSerializerOptions().SetupExtensions();
In order to distinguish inherited classes from a reference to a base class or a collection of references to a base class, a special property called discriminator can be added to the serialized json.
To describe the type of the discriminator property as well as the association between a specific inherited Type and a specific discriminator value, a discriminator convention supporting the interface IDiscriminatorConvention should be written.
Discriminator conventions can be registered to the discriminator convention registry accessible from the JsonSerilizedOptions.
Several conventions can be registered. When registering an inherited Type, an attempt to register it to each convention, begining with the last convention register. The first convention to accept to register a specific type will stop the registration process. It means that different type inheritance hierarchy could serialize/deserialize their discriminator property in a different way.
The library offers 1 built-in discriminator convention:
- DefaultDiscriminatorConvention: the discriminator value is defined by decorating classes with the attribute JsonDiscriminatorAttribute
This built-in convention setup the convention property name to $type
This type of the discriminator value is configured via the generic parameter of the convention class. The value type passed to the JsonDiscriminatorAttribute must match this type.
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
}
[JsonDiscriminator(1234)]
public class WeatherForecastDerived : WeatherForecast
{
public int WindSpeed { get; set; }
}
JsonSerializerOptions options = new JsonSerializerOptions();
options.SetupExtensions();
DiscriminatorConventionRegistry registry = options.GetDiscriminatorConventionRegistry();
registry.ClearConventions();
registry.RegisterConvention(new DefaultDiscriminatorConvention<int>(options, "_t"));
registry.RegisterType<WeatherForecastDerived>();
string json = JsonSerializer.Serialize<WeatherForecast>(weatherForecastDerived, options);
{
"_t": 1234,
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"WindSpeed": 35
}
- Auto (default value): the discriminator property is written only the declared type and the actual type are different.
- Always: the discriminator property is forced to always be written.
- Never: he discriminator property is forced to never be written
JsonSerializerOptions options = new JsonSerializerOptions();
options.SetupExtensions();
DiscriminatorConventionRegistry registry = options.GetDiscriminatorConventionRegistry();
registry.DiscriminatorPolicy = DiscriminatorPolicy.Always;
In the class to serialize, if a method exists which signature is bool ShouldSerialize[PropertyName](), it will be called to conditionally serialize the matching property.
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
public bool ShouldSerializeSummary()
{
return !string.IsNullOrEmpty(Summary);
}
}