-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
System.Text.Json hardcodes polymorphic serialization when serializing root-level values of type object, regardless of whether the registered JsonConverter<object> supports polymorphism. While this is consistent with the semantics of the built-in converter for object, it can result in inconsistent serialization contracts when used in conjunction with custom converters, depending on whether the value is serialized at the root level or as a nested node:
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
var options = new JsonSerializerOptions { Converters = { new CustomObjectConverter() } };
Console.WriteLine(JsonSerializer.Serialize<object>(0, options)); // Prints 0, custom converter not honored
Console.WriteLine(JsonSerializer.Serialize<object[]>(new object[] { 0 }, options)); // Prints [42], custom converter honored
public class CustomObjectConverter : JsonConverter<object>
{
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
=> writer.WriteNumberValue(42);
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> throw new NotImplementedException();
}This issue proposes that we change the root-level behavior for custom object converters so that it no longer defaults to polymorphism (unless a polymorphic converter is being used). This will make serialization contracts consistent, regardless of whether the instance is serialized as a root-level value or as a nested value.
This change might be a breaking change for users that rely on the current behavior. As a workaround, users can get back the existing polymorphic behavior by calling into one of the untyped JsonSerializer APIs, explicitly passing the runtime type of the value as the inputType parameter:
-JsonSerializer.Serialize<object?>(value, options);
+Type runtimeType = value?.GetType() ?? typeof(object);
+JsonSerializer.Serialize(value, runtimeType, options);