【问题标题】:How to exclude a property from being serialized in System.Text.Json.JsonSerializer.Serialize() using a JsonConverter如何使用 JsonConverter 在 System.Text.Json.JsonSerializer.Serialize() 中排除属性被序列化
【发布时间】: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();
    }
}

【问题讨论】:

    标签: c# json serialization .net-core


    【解决方案1】:

    选项 1 - 转换为接口

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

      public interface IBook
      {
          public int Id { get; set; }
          public string Name { get; set; }
      }
      
    2. 在原类class Book : IBook上实现它

    3. 使用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 类,这很有用。

    1. 创建LiteBook 类:

      public class LiteBook
      {
          public int Id { get; set; }
          public string Name { get; set; }
      }
      
    2. 创建映射配置:

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

      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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-25
      • 1970-01-01
      相关资源
      最近更新 更多