JsonMergePatch and .NET6
12/18/2021
3 minutes
In a previous post I have shown how JsonMergePatch library can be used with console and Asp.Net Core applications.
In this post I will focus on using it with the source generator hand-in-hand of System.Text.Json. Before getting into the details, a new [Patchable]
attribute is introduced ease the work:
Patchable
Certain use-cases require to generate wrapper types with the source generation for assemblies that do not directly use Patch<T>
(where T is the wrapped source type). This could be a reason for having separate assemblies for entity types, or because of the need of stacking multiple source generators on top of each other.In this case types may be attributed with [Patchable]
attribute:
[Patchable] public class WeatherForecast { //... }
[Patchable]
makes sure to generate wrapper types for source types not used in HTTP requests or method arguments of Patch<T>
.
Using it with System.Text.Json source generation
In order to use multiple source generators (System.Text.Json on top of the entities generated by JsonMergePatch), we need to stack them. Today the only way to do it is by enforcing a build order between two projects, while adding the first source generator to the first project built, and the second one to the second project built. To make sure JsonMergePatch source generator works with System.Text.Json's source generator, create two projects:
An entities class library
Add a .NET 6, Asp.Net Core executable (referred as the
Application
)
Make sure Application
references the Entities
project. Add entity/POCO/Patchable types to the Entities
project. Mark these types with [Patchable]
attribute.Add the following nuget packages to the Entities
project:
LaDeak.JsonMergePatch.SourceGenerator
LaDeak.JsonMergePatch.Abstractions
This will generate the wrapper types in the Entities
project.
In the Application
project add a JsonSerializerContext
as detailed by System.Text.Json library. In the [JsonSerializable]
attributes, list the generated wrapper types from the Entities
project using LaDeak.JsonMergePatch.Generated.Safe{TypeName}
naming pattern.
[JsonSerializable(typeof(LaDeak.JsonMergePatch.Generated.SafeAspNetCoreMinimal.Entities.WeatherForecastWrapped))] [JsonSerializable(typeof(LaDeak.JsonMergePatch.Generated.SafeAspNetCoreMinimal.Entities.CitiesDataWrapped))] public partial class SampleJsonContext : JsonSerializerContext { }
For Asp.Net Core applications, create a new JsonOptions
object and extend the serialization options with the derived JsonSerializerContext
type. Pass the JsonOptions
object to the constructor of JsonMergePatchInputReader
.
var mvcBuilder = builder.Services.AddControllers().AddMvcOptions(options => { LaDeak.JsonMergePatch.Abstractions.JsonMergePatchOptions.Repository = LaDeak.JsonMergePatch.Generated.SafeAspNetCoreMinimal.TypeRepository.Instance; var jsonOptions = new Microsoft.AspNetCore.Http.Json.JsonOptions(); jsonOptions.SerializerOptions.AddContext<SampleJsonContext>(); options.InputFormatters.Insert(0, new JsonMergePatchInputReader(jsonOptions)); });
By default JsonMergePatchInputReader
uses static value of LaDeak.JsonMergePatch.Abstractions.JsonMergePatchOptions.Repository. In case it needs to be overridden, JsonMergePatchInputReader
offers a second constructor parameter, in which any custom TypeRepository
may be passed as an argument.
Samples
Sample web applications can be found in the sample folder