[英]How to serialize interface default property?
这个问题复活了,因为我在评论“我的坏”时错了。 这个问题是存在的,或者我错过了什么。 对于流行的需求,这里是一个最小的可重现示例:
我有一个带有默认属性的接口:
public interface INotification
{
// ...
Severity Severity { get; set; } // Severity is an Enum
// ...
public string SeverityName => Severity.ToString().ToLower();
}
我有一个实现 class
public class Notification : INotification
{
// ...
public Severity Severity { get; set; }
// ...
// This class does not override default implementation of SeverityName
}
问题
通知 class 没有SeverityName
属性......这很令人惊讶,但我可以忍受,通过INotification
接口访问通知实例。
但是,我想用System.Text.Json
序列化这个 class 。 我怎样才能序列化SeverityName
属性呢?
这对于 .NET Core 3.1 中的System.Text.Json
是不可能的,除非为Notification
编写自定义JsonConverter
。 序列化器序列化一个类型的公共属性,但默认接口属性没有实现为 class 属性,而是通过某种扩展机制实现。 请参阅规范: 默认接口方法
添加对虚拟扩展方法的支持 - 具有具体实现的接口中的方法。 .
只有当默认接口属性被覆盖时,实例属性才会真正添加到实现的具体类型中,从而变为可序列化的。
为了确认,如果我们从您的小提琴中修改类如下:
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
中有。 由于缺少要序列化的属性,序列化程序不会 output 一个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 中,无法覆盖具体 class 中的默认接口方法并调用基本接口方法。 有关详细信息,请参阅如何调用默认方法而不是具体实现、使成员虚拟化可防止调用默认接口实现并导致 C# 8 中的 StackOverflowException和默认接口实现和base()
调用。
为了解决这个问题,我向封装默认逻辑的INotification
添加了一个static
接口方法,然后从默认接口实例方法和覆盖 class 方法中调用它。
可以在此处找到显示上述内容的演示小提琴。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.