system.text.json序列化对抽象成员无效
我有以下接口及其实现(使用JSON Serializers for Newtonsoft.json
和 System.text.json
):
public interface IAmount {
decimal Value { get; }
}
[Newtonsoft.Json.JsonConverter(typeof(NewtonsoftJsonConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(SystemTextJsonConverter))]
public class Amount : IAmount {
public Amount(decimal value) {
Value = value;
}
public decimal Value { get; }
}
public class NewtonsoftJsonConverter : Newtonsoft.Json.JsonConverter {
public override bool CanConvert(Type objectType) => objectType.IsAssignableTo(typeof(IAmount));
public override object? ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object? existingValue, Newtonsoft.Json.JsonSerializer serializer) {
throw new NotImplementedException();
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object? value, Newtonsoft.Json.JsonSerializer serializer) {
writer.WriteRawValue(((IAmount?)value)?.Value.ToString());
}
}
public class SystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<object> {
public override bool CanConvert(Type typeToConvert) => typeToConvert.IsAssignableTo(typeof(IAmount));
public override object Read(ref System.Text.Json.Utf8JsonReader reader, Type typeToConvert, System.Text.Json.JsonSerializerOptions options) {
throw new NotImplementedException();
}
public override void Write(System.Text.Json.Utf8JsonWriter writer, object value, System.Text.Json.JsonSerializerOptions options) {
writer.WriteRawValue(((IAmount)value).Value.ToString());
}
}
如果我的对象是类型金额。例如(在每行旁边的注释中输出中的输出):
var foo = new Amount(10);
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(foo)); // 10
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(foo)); // 10
但是,如果对象的类型为 iamount
,则可以为 newtonsoft.json
而不是 System。 text.json
。例如:
IAmount foo = new Amount(10);
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(foo)); // 10
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(foo)); // {"Value":10}
使用 System.text.json
时,您可以看到输出有所不同。我尝试针对 cancovert
方法进行断点,但是从未调用。
我可以通过添加 [System.Text.json.Serialization.jsonConverter(typeof(SystemTextJsonConverter))]
属性来解决此问题,但是我不想这样做。有谁知道无需修改界面而不必修改界面的替代解决方案?
请注意,切换到Newtonsoft不是一个选择。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是设计的。 system.text.json有意不支持序列化过程中的多态性,除非明确声明要序列化的对象为
object
或(从.net 7 开始) opt-opt-opt-opt-in启用。来自 docs :虽然文档仅指出属性尚未序列化,但我相信,由于system.text.json是内部基于合同的序列化器,因此它在序列化时使用声明类型的整个合同派生类型。因此,元数据(包括
jsonConverterAttribute
和任何其他 json属性已应用的)以及通过反映声明的类型(此处iamount
)而不是实际的类型(此处noce
nocal)来获取属性。
)。那么,您可以选择解决此限制?
首先,如果
iamount
仅以量为
,则可以引入jsonConverter
,它总是将一种类型序列化为其他兼容类型:然后将其应用于
iamount
:或将其添加到 iamount 无法修改,或者如果在不同情况下使用不同的混凝土类型
=“ nofollow noreferrer”>
jsonserialializaizoptions.converters
如果 #2 此处和在这里。其次,如果您根本不在乎避免化,并且希望将所有值声明为接口被序列化为混凝土类型,则可以引入一个可以做到这一点的转换器工厂:
并直接将其应用于
iamount :
或在选项中添加:
演示小提琴#3 在这里。
This is as designed. System.Text.Json intentionally does not support polymorphism during serialization except when the object to be serialized is explicitly declared to be
object
or (starting with .NET 7) its opt-in support for polymorphism is enabled. From the docs:While the documentation only states that properties of derived classes are not serialized, I believe that, since System.Text.Json is internally a contract-based serializer, it uses the entire contract of the declared type when serializing a derived type. Thus the metadata (including
JsonConverterAttribute
and any other JSON attributes that have been applied) as well as the properties are taken by reflecting the declared type (hereIAmount
) not the actual type (hereAmount
).So, what are your options to work around this restriction?
Firstly, if
IAmount
is only ever implemented asAmount
, you could introduce aJsonConverter
which always serializes one type as some other compatible type:Then either apply it to
IAmount
:Or add it in
JsonSerializerOptions.Converters
ifIAmount
cannot be modified, or if different concrete types will be used in different circumstances:Demo fiddles #1 and #2 here and here.
Secondly, if you don't care about deserialization at all and want all values declared as interfaces to be serialized as their concrete types, you could introduce a converter factory that does just that:
And either apply it directly to
IAmount
:Or add it in options:
Demo fiddle #3 here.