Skip to content

System.Text.Json can't serialize nested closed hierarchies #132660

Description

@andrewlock

Description

Using the InferClosedTypePolymorphism in preview 7, there seems to be no way to serialize a closed hierarchy that contains multiple closed types?

e.g. given a hierarchy like this:

Pet (closed)
 |___ Cat
 |___ Dog (closed)
      |___ Labrador
      |___ Collie

then if you try to serialize a Pet instance you get an exception:

System.InvalidOperationException: Specified type 'Dog' is not a supported
derived type for the polymorphic type 'Pet'. Derived types must not be open
generic type definitions, must be assignable to the base type and cannot be 
abstract classes or interfaces unless 'JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor' 
is specified.

That seems like a big limitation? 🤔

Reproduction Steps

The following simple program reproduces the issue:

Pet data = new Labrador();
var opts = new JsonSerializerOptions { InferClosedTypePolymorphism = true };
string serialized = JsonSerializer.Serialize(data, opts);

// Type definitions
closed class Pet {}
sealed class Cat : Pet { }

closed class Dog : Pet { }
sealed class Labrador : Dog { }
sealed class Collie : Dog { }

Expected behavior

I would expect that the Pet to recursively know about all it's descendants given it's a closed hierarchy. I can understand that this can't always be possible (if you have unsealed types for example), but in the example above, the hierarchy is completely defined?

Actual behavior

System.InvalidOperationException: Specified type 'Dog' is not a supported
derived type for the polymorphic type 'Pet'. Derived types must not be open
generic type definitions, must be assignable to the base type and cannot be 
abstract classes or interfaces unless 'JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor' 
is specified.

Regression?

No, this is new functionality

Known Workarounds

I couldn't find a way to make this work with InferClosedTypePolymorphism. The only approach I could find was to fallback to manually specify the derived types on Pet, i.e. manually do what I expected the feature to do

[JsonDerivedType(typeof(Collie), typeDiscriminator: nameof(Collie))]
[JsonDerivedType(typeof(Labrador), typeDiscriminator: nameof(Labrador))]
[JsonDerivedType(typeof(Cat), typeDiscriminator: nameof(Cat))]
public closed class Pet

Configuration

❯ dotnet --version
11.0.100-preview.7.26381.103

Windows 11, x64

Other information

Related to #125449

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions