Skip to content

System.Text.Json Polymorphic Type Resolving Issue #77532

Closed
@akurone

Description

@akurone

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.Text.Jsonpartner-impactThis issue impacts a partner who needs to be kept updated

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions