Consider these model classes:
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")] [JsonDerivedType(typeof(A), typeDiscriminator: nameof(A))] [JsonDerivedType(typeof(B), typeDiscriminator: nameof(B))] interface IMyType { } class A(DependencyA dependency) : IMyType { public int X { get; set; } = dependency.GetValue(); } class B(DependencyB dependency) : IMyType { public int Y { get; set; } = dependency.GetValue(); } class DependencyA { public int GetValue() => 42; } class DependencyB { public int GetValue() => 123; } DI container used is Microsoft.DependencyInjection.
The example:
static void Main(string[] args) { IServiceCollection services = new ServiceCollection() .AddTransient<DependencyA>() .AddTransient<DependencyB>() .AddTransient<A>() .AddTransient<B>(); IServiceProvider provider = services.BuildServiceProvider(); IMyType[] items = [ provider.GetRequiredService<A>(), provider.GetRequiredService<B>() ]; Print(items); string json = JsonSerializer.Serialize(items); Console.WriteLine(json); // This line is invalid but states my intension: IMyType[]? deserialized = JsonSerializer.Deserialize<IMyType[]>(json); // ^^^^^^^^ // System.InvalidOperationException: 'Each parameter in the deserialization constructor // on type 'ConsoleAppExample.A' must bind to an object property or field on // deserialization. Each parameter name must match with a property or field on the object. // Fields are only considered when 'JsonSerializerOptions.IncludeFields' is enabled. // The match can be case-insensitive.' Print(deserialized); } static void Print(IMyType[]? items) { foreach (IMyType instance in items ?? []) { switch (instance) { case A aInstance: Console.WriteLine($"A.X = {aInstance.X}"); break; case B bInstance: Console.WriteLine($"B.Y = {bInstance.Y}"); break; } } } Beginning of output (before exception):
A.X = 42 B.Y = 123 [{"$type":"A","X":42},{"$type":"B","Y":123}] The serialized JSON output is as expected.
My question: how to make the JsonSerializer aware of ServiceProvider to create the instances using DI instead of calling constructor?
I've tried to create some PolymorphicTypeResolver but failed because I didn't find a place to inject IServiceProvider instance where it can get the derived Type and return the object for deserializer.
OK, here's my attempt:
class PolymorphicTypeResolver(IServiceProvider serviceProvider) : DefaultJsonTypeInfoResolver { static Type[] RegisteredTypes = [typeof(A), typeof(B)]; public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) { var jsonTypeInfo = base.GetTypeInfo(type, options); if (jsonTypeInfo.Type == typeof(IMyType)) { // WRONG LINE because here `type` is always `IMyType` // But where can I get the derived one? jsonTypeInfo.CreateObject = () => serviceProvider.GetRequiredService(type); jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions { TypeDiscriminatorPropertyName = "$type", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization, }; foreach (var derived in RegisteredTypes) { jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(derived, derived.Name)); } } return jsonTypeInfo; } } A native-AOT-friendly solution is preferred.