如何覆盖属性的 JSON 序列化,将值序列化为字符串而不是对象?

Posted

技术标签:

【中文标题】如何覆盖属性的 JSON 序列化,将值序列化为字符串而不是对象?【英文标题】:How can I override the JSON serialization for a property, to serialize the value as a string instead of an object? 【发布时间】:2021-03-04 12:41:26 【问题描述】:

使用 .Net 4.8,我的所有实体框架类都继承自 AbstractBase:

public abstract class AbstractBase

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public virtual int Id  get; set; 

    public virtual DateTime CreatedTimestamp  get; set; 

    public virtual DateTime UpdatedTimestamp  get; set; 

    [Required]
    public virtual int CreatedBy  get; set; 

    [Required]
    public virtual int LastModifiedBy  get; set; 

    public virtual bool ActiveFlag  get; set; 

    [JsonIgnore]
    public virtual User CreatedByUser  get; set; 

    [JsonIgnore]
    public virtual User LastModifiedByUser  get; set; 

    public string ToActiveInactive()
    
        return this.ActiveFlag ? "Active" : "Inactive";
    

现在你可以看到我忽略了CreatedByUserLastModifiedByUser。 我想做的是以某种方式替换 User 对象 具有名为 FullName 的属性 User 对象可以提供的类似

User test = new User();
test.GetFullName();

我的 User 对象已经有了这个,所以我的问题是有没有办法做到这一点,这样我就不必在每次想要序列化实体类时编写更多相同逻辑的实现?

【问题讨论】:

"用用户对象可以提供的名为 FullName 的属性替换用户对象是什么意思" 是什么意思?您的示例代码没有解释这一点。你的意思是你想给AbstractBase一个属性string FullName => CreatedByUser.GetFullName(); 【参考方案1】:

您可以创建一个自定义JsonConverter<T>,就像Newtonsoft documentation 中显示的那样,将您的User 类序列化为字符串:

public class UserConverter : JsonConverter<User>

    public override void WriteJson(JsonWriter writer, User value, JsonSerializer serializer)
    
        writer.WriteValue(value.GetFullName());
    

    public override User ReadJson(JsonReader reader, Type objectType, User existingValue, bool hasExistingValue, JsonSerializer serializer)
    
        var fullName = serializer.Deserialize<string>(reader);
        // Or construct a User and return it, if you can do so from the fullName.
        throw new NotImplementedException();
    

请注意,您的问题没有为我提供足够的信息来编写 ReadJson() 方法。如果需要支持反序列化,则需要通过从fullName 字符串构造User 来完全实现此方法。

现在,要使用转换器,您有多种选择:

    如果您只想将AbstractBaseUser 属性序列化为字符串,请使用JsonConverterAttribute 将转换器应用于User 属性,如下所示:

    public abstract class AbstractBase
    
        [JsonConverter(typeof(UserConverter))]
        public virtual User CreatedByUser  get; set; 
    
        [JsonConverter(typeof(UserConverter))]
        public virtual User LastModifiedByUser  get; set; 
    
        // Remaining properties and methods unchanged
    

    如果您希望User of 的所有实例都被序列化为字符串,您可以将其直接应用于User,如下所示:

    [JsonConverter(typeof(UserConverter))]
    public class User
    
        // Properties and methods unchanged
    

    或者,您可以将其添加到您的 asp.net-web-api2 全局 JsonSerializerSettings.Converters。要在 .NET Framework 中执行此操作,请参阅 Andrei 的 this answer 到 Json.net global settings。对于 .NET Core 2.x,请参阅 this answer 到 Customizing response serialization in ASP.NET Core MVC 由 Métoule。

    .NET Core 3.x 及更高版本使用不同的序列化程序system.text.json,因此您必须在该版本中使用不同的方法,或者恢复使用 Json.NET,如 Where did IMvcBuilder AddJsonOptions go in .Net Core 3.0? 所示.

演示小提琴here.

【讨论】:

以上是关于如何覆盖属性的 JSON 序列化,将值序列化为字符串而不是对象?的主要内容,如果未能解决你的问题,请参考以下文章

序列化为 JSON 字符串时如何修复 OutOfMemoryException?

为啥使用 Json.NET 序列化为 json 字符串时缺少 DriveInfo 的属性?

如何在 json 序列化时使用 DefaultContractResolver 覆盖具有字符串值的复杂类型属性

JSON.NET 将对象 序列化为 JSON字符串时,能否指定,某个属性不被转化。

使用 kotlinx.serialization 将 json 对象属性反序列化为字符串

在 Azure 移动应用服务后端将字符串属性反序列化为 Json 对象