Description
Description
hi there,
as discussed in #dotnet/aspnetcore/issues/41399 SystemTextJsonOutputFormatter
calls serializer with derived type of the object and this seems reasonable (as per the comment in the code). when called like this, even the attributes introduced in #63747 are present, the serializer does not output type discriminators. in my use case this causes a deserialization problem: without the discriminators, receiving side cannot use the json produced (by the endpoint). i have a workaround but it seems like a misuse and has its own drawback: i need to change my class design for it to work.
Reproduction Steps
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
try
{
Console.WriteLine($"{nameof(DerivedA)}, showing workaround:");
Console.WriteLine(JsonSerializer.Serialize(new DerivedA(), typeof(DerivedA)));
Console.WriteLine();
Console.WriteLine($"{nameof(DerivedB)}, like STJOutputFormatter:");
//STJOutputFormatter calls serializer this way https://github.com/dotnet/aspnetcore/issues/41399
Console.WriteLine(JsonSerializer.Serialize(new DerivedB(), typeof(DerivedB)));
Console.WriteLine();
Console.WriteLine($"{nameof(DerivedB)}, with base type generic works as expected:");
Console.WriteLine(JsonSerializer.Serialize<SomeBaseType>(new DerivedB()));
Console.WriteLine();
Console.WriteLine($"{nameof(DerivedB)}, with base type info works as expected:");
Console.WriteLine(JsonSerializer.Serialize(new DerivedB(), typeof(SomeBaseType)));
Console.WriteLine();
Console.WriteLine($"{nameof(DerivedC)}, workaround is crippled:");
Console.WriteLine(JsonSerializer.Serialize(new DerivedC(), typeof(DerivedC)));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
[JsonPolymorphic/*properties of this attribute are not altering the result in this case*/,
JsonDerivedType(typeof(DerivedA), nameof(DerivedA)),
JsonDerivedType(typeof(DerivedB), nameof(DerivedB)),
JsonDerivedType(typeof(DerivedC), nameof(DerivedC))]
public abstract class SomeBaseType { public int Prop1 { get; set; } = 1; }
[JsonDerivedType(typeof(DerivedA), nameof(DerivedA))] // this extra attribute is **the** workaround
public class DerivedA : SomeBaseType { public int Prop2 { get; set; } = 2; }
//no extra attribute
public class DerivedB : SomeBaseType { public int Prop3 { get; set; } = 3; }
[JsonDerivedType(typeof(DerivedC), nameof(DerivedC))]
//sealing this type will throw: https://github.com/dotnet/runtime/blob/v7.0.0-rc.2.22472.3/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PolymorphicTypeResolver.cs#L174
//but this is a valid use case here
public sealed class DerivedC : SomeBaseType { public int Prop4 { get; set; } = 4; }
Expected behavior
when the base type is decorated with required attributes (or other tools mentioned in #63747 are utilized to provide polymorphic information) the serializer should respect those even the call is made with direct type and add correct discriminator to output.
2nd case of the repro code should print:
DerivedB, like STJOutputFormatter:
{"$type":"DerivedB","Prop3":3,"Prop1":1}
Actual behavior
when called with the derived type serializer is not adding discriminators to output (result of the repro code):
DerivedA, showing workaround:
{"$type":"DerivedA","Prop2":2,"Prop1":1}
DerivedB, like STJOutputFormatter:
{"Prop3":3,"Prop1":1}
DerivedB, with base type generic works as expected:
{"$type":"DerivedB","Prop3":3,"Prop1":1}
DerivedB, with base type info works as expected:
{"$type":"DerivedB","Prop3":3,"Prop1":1}
DerivedC, workaround is crippled:
Specified type 'DerivedC' does not support polymorphism. Polymorphic types cannot be structs, sealed types, generic types or System.Object.
Regression?
no
Known Workarounds
see the repro code.
Configuration
SDK: 7.0.100-rc.2.22477.23
Other information
No response