这个问题复活了,因为我在评论"我的坏"时错了。 这个问题就在那里,或者我错过了一些东西。对于大众需求,这里有一个最小的可重现示例:
最小可重现示例
我有一个带有默认属性的接口:
public interface INotification
{
// ...
Severity Severity { get; set; } // Severity is an Enum
// ...
public string SeverityName => Severity.ToString().ToLower();
}
我有一个实现类
public class Notification : INotification
{
// ...
public Severity Severity { get; set; }
// ...
// This class does not override default implementation of SeverityName
}
问题
通知类没有SeverityName
属性...这很令人惊讶,但我可以忍受这一点,通过INotification
接口访问通知实例。
但是,我想用System.Text.Json
序列化这个类。如何序列化SeverityName
属性?
这在 .NET Core 3.1 中System.Text.Json
是不可能的,只能为Notification
编写自定义JsonConverter
。序列化程序序列化类型的公共属性,但默认接口属性不是作为类属性实现的,而是通过某种扩展机制实现的。请参阅规范:默认接口方法
添加对虚拟扩展方法的支持 - 具有具体实现的接口中的方法。
仅当重写默认接口属性时,实例属性才会实际添加到实现具体类型中,从而变得可序列化。
为了确认,如果我们按如下方式修改您的小提琴中的类:
public interface INotification
{
Severity Severity { get; set; }
string Message { get; set; }
static string MakeDefaultSeverityName<TNotification>(TNotification notification) where TNotification : INotification => notification?.Severity.ToString().ToLower();
public string SeverityName => MakeDefaultSeverityName(this);
}
public class Notification : INotification
{
public Severity Severity { get; set; }
public string Message { get; set; }
}
public class NotificationWithOverride : Notification
{
public string SeverityName => INotification.MakeDefaultSeverityName(this);
}
并使用反射打印出两种类型的属性和方法:
Console.WriteLine("Properties of {0}: {1}", typeof(TNotification).Name, string.Join(", ", typeof(TNotification).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Select(p => p.Name)));
Console.WriteLine("Methods of {0}: {1}", typeof(TNotification).Name, string.Join(", ", typeof(TNotification).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Select(p => p.Name)));
我们得到以下Notification
结果:
Properties of Notification: Severity, Message
Methods of Notification: get_Severity, set_Severity, get_Message, set_Message, GetType, MemberwiseClone, Finalize, ToString, Equals, GetHashCode
对于NotificationWithOverride
:
Properties of NotificationWithOverride: SeverityName, Severity, Message
Methods of NotificationWithOverride: get_SeverityName, get_Severity, set_Severity, get_Message, set_Message, GetType, MemberwiseClone, Finalize, ToString, Equals, GetHashCode
请注意,Notification
中没有对应于SeverityName
的实例属性或方法 - 但在NotificationWithOverride
中存在。 缺少要序列化的属性,序列化程序不输出SeverityName
值。
笔记:
在这方面,
System.Text.Json
与其他序列化程序一致;它和 Json.NET 生成完全相同的 JSON:{"Severity":0,"Message":"Message"}
Notification
.{"SeverityName":"info","Severity":0,"Message":"Message"}
NotificationWithOverride
.
有了 Json.NET,就可以创建一个自定义合约解析器,该解析器会自动添加缺少的默认属性,但
System.Text.Json
没有对其合约解析器的公共访问权限;有关确认,请参阅问题System.Text.Json API是否有类似IContractResolver的东西,答案是,目前不是。在 c# 8.0 中,无法重写具体类中的默认接口方法并调用基接口方法。 有关详细信息,请参阅如何调用默认方法而不是具体实现、使成员成为虚拟可防止调用默认接口实现并导致 C# 8 中的 StackOverflowException和默认接口实现和
base()
调用。为了解决此问题,我在
INotification
中添加了一个封装默认逻辑的static
接口方法,然后从默认接口实例方法和重写类方法调用它。
可以在此处找到显示上述内容的演示小提琴。