繁体   English   中英

如何序列化接口默认属性?

[英]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值。

笔记:

  1. System.Text.Json在这方面与其他序列化器一致; 它和 Json.NET 都生成完全相同的 JSON:

    • {"Severity":0,"Message":"Message"}用于Notification
    • {"SeverityName":"info","Severity":0,"Message":"Message"}用于NotificationWithOverride
  2. 使用 Json.NET 可以创建一个自定义合约解析器,自动添加缺少的默认属性,但System.Text.Json没有对其合约解析器的公共访问权限; 如需确认,请参阅问题System.Text.Json API 是否有类似 IContractResolver的答案,目前不是

  3. 在 c# 8.0 中,无法覆盖具体 class 中的默认接口方法并调用基本接口方法。 有关详细信息,请参阅如何调用默认方法而不是具体实现使成员虚拟化可防止调用默认接口实现并导致 C# 8 中的 StackOverflowException默认接口实现和base()调用

    为了解决这个问题,我向封装默认逻辑的INotification添加了一个static接口方法,然后从默认接口实例方法和覆盖 class 方法中调用它。

可以在此处找到显示上述内容的演示小提琴。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM