如何使用 JsonConverter 在 System.Text.Json.JsonSerializer.Serialize() 中排除属性被序列化

Posted

技术标签:

【中文标题】如何使用 JsonConverter 在 System.Text.Json.JsonSerializer.Serialize() 中排除属性被序列化【英文标题】:How to exclude a property from being serialized in System.Text.Json.JsonSerializer.Serialize() using a JsonConverter 【发布时间】:2020-02-22 06:59:16 【问题描述】:

我希望能够在使用 System.Text.Json.JsonSerializer 进行序列化时排除属性。我不想在任何我想这样做的地方使用JsonIgnore 属性。我希望能够通过某种目前不存在的 Fluent API 来定义我想在序列化期间排除的属性。

我能找到的唯一选择是定义一个 JsonConverter 并将其添加到 JsonSerializerOptions 上的转换器列表中,我将其传递给 Serialize() 方法,如下所示:

var options = new JsonSerializerOptions();
options.Converters.Add(new BookConverter());
json = JsonSerializer.Serialize(book, options);

在 JsonConverter 中,我必须自己使用 Utf8JsonWriter 编写整个 JSON 表示,不包括我不想序列化的属性。仅仅能够排除一个属性需要做很多工作。虽然 JsonConverter 是 .NET 团队的一项出色的可扩展性功能,但对于我的用例而言,它的级别太低了。有谁知道无需自己写出 JSON 表示即可排除该属性的任何其他方法?

我不想做以下事情:

使用属性,或在运行时动态添加属性 将属性的访问修饰符更改为 privateprotected 使用 3rd 方库,因为如果我使用 Json.NET,我的问题是可以解决的。

例子:

class Program

    void Main()
    
        // We want to serialize Book but to ignore the Author property
        var book = new Book()  Id = 1, Name = "Calculus", Author = new Author() ;

        var json = JsonSerializer.Serialize(book);
        // Default serialization, we get this:
        // json =  "Id": 1, "Name": "Calculus", "Author":  

        // Add our custom converter to options and pass it to the Serialize() method
        var options = new JsonSerializerOptions();
        options.Converters.Add(new BookConverter());
        json = JsonSerializer.Serialize(book, options);
        // I want to get this:
        // json =  Id: 1, Name: "Calculus" 
    


public class Author  

public class Book

    public int Id  get; set; 
    public string Name  get; set; 
    public Author Author  get; set; 


public class BookConverter : JsonConverter<Book>

    public override Book Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    
        // Use default implementation when deserializing (reading)
        return JsonSerializer.Deserialize<Book>(ref reader, options);
    

    public override void Write(Utf8JsonWriter writer, Book value, JsonSerializerOptions options)
    
        // Serializing. Here we have to write the JSON representation ourselves
        writer.WriteStartObject();

        writer.WriteNumber("Id", value.Id);
        writer.WriteString("Name", value.Name);
        // Don't write Author so we can exclude it

        writer.WriteEndObject();
    

【问题讨论】:

【参考方案1】:

选项 1 - 转换为接口

    描述所需对象结构的提取接口。

    public interface IBook
    
        public int Id  get; set; 
        public string Name  get; set; 
    
    

    在原类class Book : IBook上实现它

    使用string Serialize(object value, Type inputType, JsonSerializerOptions options = null);的跟随重载

    json = JsonSerializer.Serialize(book, typeof(IBook), options);
    

    如果您要序列化 ​​Books(复数)的数组,则需要将 typeof(IEnumerable&lt;IBook&gt;) 作为参数传递。

选项 2 - 使用 AutoMapper

如果您无权访问原始 Book 类,这很有用。

    创建LiteBook 类:

    public class LiteBook
    
        public int Id  get; set; 
        public string Name  get; set; 
    
    

    创建映射配置:

    var config = new MapperConfiguration(cfg => 
        cfg.CreateMap<Book, LiteBook>();
    );
    

    映射并序列化

    json = JsonSerializer.Serialize(new Mapper(config).Map<LiteBook>(book), options)
    

【讨论】:

MapperConfiguration的命名空间是什么?【参考方案2】:

所以我偶然发现了一篇文章,该文章演示了如何在新的 System.Text.Json 命名空间中使用 JsonDocument 对象,它是 Fluent API 的下一个最佳选择。这是如何解决这个问题的。

BookConverter.Write() 方法:

public override void Write(Utf8JsonWriter writer, Book value, JsonSerializerOptions options)

    writer.WriteStartObject();

    using (JsonDocument document = JsonDocument.Parse(JsonSerializer.Serialize(value)))
    
        foreach (var property in document.RootElement.EnumerateObject())
        
            if (property.Name != "Author")
                property.WriteTo(writer);
        
    

    writer.WriteEndObject();

【讨论】:

这行得通,但如果它应该运行得很快,性能就不那么好了。 value 在被解析为JsonDocument 之前以默认方式序列化,只有这样我们才能真正开始手动序列化(支持忽略属性)该值。所以执行成本几乎翻了2-2.5倍。【参考方案3】:

您可以简单地忽略这样的属性:

public class Book

    public int Id  get; set; 
    public string Name  get; set; 
    [JsonIgnore]
    public Author Author  get; set; 

参考:https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-ignore-properties?pivots=dotnet-6-0

【讨论】:

以上是关于如何使用 JsonConverter 在 System.Text.Json.JsonSerializer.Serialize() 中排除属性被序列化的主要内容,如果未能解决你的问题,请参考以下文章

冻结了如何在***模型上分配我自己的 JsonConverter?

如何在 JSON.NET 中实现自定义 JsonConverter?

如何在 .net core 3.1 中的 Newtonsoft JsonConverter 中注入依赖项

.net HttpClient 与自定义 JsonConverter

Web API - JsonConverter - 自定义属性

JsonConverter 等效于使用 System.Text.Json