Skip to content

[Breaking change]: System.Text.Json no longer hardcodes polymorphism for root-level object types #30758

Description

@eiriktsarpalis

Description

Using default configuration, System.Text.Json will serialize values of type objectusing polymorphism. This becomes less consistent once users register a custom converter for object, as witnessed in the following example:

usingSystem;usingSystem.Text.Json;usingSystem.Text.Json.Serialization;varoptions=newJsonSerializerOptions{Converters={newCustomObjectConverter()}};Console.WriteLine(JsonSerializer.Serialize<object>(0,options));// Prints 0, custom converter not honoredConsole.WriteLine(JsonSerializer.Serialize<object[]>(newobject[]{0},options));// Prints [42], custom converter honoredpublicclassCustomObjectConverter:JsonConverter<object>{publicoverridevoidWrite(Utf8JsonWriterwriter,objectvalue,JsonSerializerOptionsoptions)=>writer.WriteNumberValue(42);publicoverrideobjectRead(refUtf8JsonReaderreader,TypetypeToConvert,JsonSerializerOptionsoptions)=>thrownewNotImplementedException();}

In short, System.Text.Json has historically been hardcoding polymorphism for root-level object values but not using polymorphism for nested object values. Starting with .NET 7 RC1, this behavior has been changed so that custom converters never use polymorphism.

Version

.NET 7 RC 1

Previous behavior

Using the custom object converter defined above, the code

varoptions=newJsonSerializerOptions{Converters={newCustomObjectConverter()}};JsonSerializer.Serialize<object>(0,options);

Will serialize as 0, since the serializer will use polymorphism and ignore the custom converter.

New behavior

Using the custom object converter defined above, the code

varoptions=newJsonSerializerOptions{Converters={newCustomObjectConverter()}};JsonSerializer.Serialize<object>(0,options);

Will serialize as 42, since the serializer will always consult the custom converter and not use polymorphism.

Type of breaking change

  • Binary incompatible: Existing binaries may encounter a breaking change in behavior, such as failure to load/execute or different run-time behavior.
  • Source incompatible: Source code may encounter a breaking change in behavior when targeting the new runtime/component/SDK, such as compile errors or different run-time behavior.

Reason for change

Inconsistency on serialization contracts for a given type, depending on whether it is being serialized as a root-level value or a nested value.

Recommended action

Users can get back polymorphism for root-level values by invoking one of the untyped serialization methods:

varoptions=newJsonSerializerOptions{Converters={newCustomObjectConverter()}};JsonSerializer.Serialize(0,inputType:typeof(int),options);// serializes as 0

Feature area

Core .NET libraries

Affected APIs

No response

Metadata

Metadata

Assignees

Labels

🏁 Release: .NET 7Work items for the .NET 7 releasebinary incompatibleExisting binaries may encounter a breaking change in behavior.breaking-changeIndicates a .NET Core breaking change

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions