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

Tar*_*her 11 c# serialization json .net-core

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

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

var options = new JsonSerializerOptions();
options.Converters.Add(new BookConverter());
json = JsonSerializer.Serialize(book, options);
Run Code Online (Sandbox Code Playgroud)

在 JsonConverter 中,我必须自己使用 a 编写整个 JSON 表示Utf8JsonWriter,不包括我不想序列化的属性。仅仅能够排除一个属性就需要做很多工作。虽然 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Con*_*sky 7

选项 1 - 投射到界面

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

    public interface IBook
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在原始类上实现它 class Book : IBook

  3. 使用以下重载 string Serialize(object value, Type inputType, JsonSerializerOptions options = null);

    json = JsonSerializer.Serialize(book, typeof(IBook), options);
    
    Run Code Online (Sandbox Code Playgroud)

    如果要序列化Books(复数)数组,则需要typeof(IEnumerable<IBook>)作为参数传递。

选项 2 - 使用 AutoMapper

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

  1. 创建LiteBook类:

    public class LiteBook
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建映射配置:

    var config = new MapperConfiguration(cfg => {
        cfg.CreateMap<Book, LiteBook>();
    });
    
    Run Code Online (Sandbox Code Playgroud)
  3. 映射并序列化

    json = JsonSerializer.Serialize(new Mapper(config).Map<LiteBook>(book), options)
    
    Run Code Online (Sandbox Code Playgroud)


Tar*_*her 5

所以我偶然发现了一篇文章,它演示了如何JsonDocument在新System.Text.Json命名空间中使用对象,它是 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();
}
Run Code Online (Sandbox Code Playgroud)

  • 这是可行的,但如果它运行得很快,性能就不太好。“value”在解析为“JsonDocument”之前会按默认方式序列化,只有这样我们才能真正开始手动序列化(支持属性忽略)该值。所以执行成本几乎翻了2-2.5倍。 (6认同)

sam*_*y34 5

.Net 7 使得更灵活、动态地控制哪些属性被序列化成为可能。请参阅此处的官方博客文章:

https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-7/#example-conditional-serialization