As mentiond in #40914 (comment), there should be runtime type checks before calling the property setter with the value returned from a custom converter. This should only be necessary when the converter type (like object / interface / base class) is not identical with the property type.
Here is what can happen without
public static void Reinterpret()
{
var obj = new ObjectWrapper
{
Object = new WrittenObject
{
Int = 123,
String = "Hello",
}
};
obj = JsonSerializer.Deserialize<ObjectWrapper>(JsonSerializer.Serialize(obj));
Debug.WriteLine(obj.GetType().GetProperty(nameof(ObjectWrapper.Object)).PropertyType.FullName);
// System.Text.Json.Serialization.Tests.CustomConverterTests+WrittenObject
Debug.WriteLine(obj.Object.GetType().FullName);
// System.Text.Json.Serialization.Tests.CustomConverterTests+ReadObject
Debug.WriteLine(Unsafe.As<ReadObject>(obj.Object).Double);
// 3.141592653589793
Debug.WriteLine(obj.Object.String.Length);
// An unhandled exception of type 'System.AccessViolationException' occurred in System.Text.Json.Tests.dll
// Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
}
public class ReinterpretConverter : JsonConverter<object>
{
public override bool CanConvert(Type typeToConvert)
=> true;
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
JsonSerializer.Deserialize<WrittenObject>(ref reader, options);
return new ReadObject{ Double = Math.PI };
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
JsonSerializer.Serialize<WrittenObject>(writer, (WrittenObject)value, options);
}
}
private class ObjectWrapper
{
[JsonConverter(typeof(ReinterpretConverter))]
public WrittenObject Object { get; set; }
}
private class WrittenObject
{
public string String { get; set; }
public int Int { get; set; }
}
private class ReadObject
{
public double Double { get; set; }
}
As mentiond in #40914 (comment), there should be runtime type checks before calling the property setter with the value returned from a custom converter. This should only be necessary when the converter type (like object / interface / base class) is not identical with the property type.
Here is what can happen without