简体   繁体   English

C#.Net Core 3.1 System.Text.Json 序列化时忽略空集合

[英]C# .Net Core 3.1 System.Text.Json Ignore empty collection in serialization

Using Newtonsoft we had a custom resolver for ignoring empty collections.使用 Newtonsoft,我们有一个自定义解析器来忽略空的 collections。 Is there any equivalent configuration for the new system.text.json in.Net core 3.1新的system.text.json in.Net core 3.1是否有任何等效配置

Conditionally ignore a property from .NET5's official How to migrate from Newtonsoft.Json to System.Text.Json from 2020 Dec 14 states: 有条件地忽略.NET5 的官方属性How to migrate from Newtonsoft.Json to System.Text.Json from 2020 Dec 14 指出:

System.Text.Json provides the following ways to ignore properties or fields while serializing: System.Text.Json 提供了以下方法来在序列化时忽略属性或字段:

  • The [JsonIgnore] attribute (...). [JsonIgnore] 属性 (...)。
  • The IgnoreReadOnlyProperties global option (...). IgnoreReadOnlyProperties 全局选项 (...)。
  • (...) JsonSerializerOptions.IgnoreReadOnlyFields global (...) (...) JsonSerializerOptions.IgnoreReadOnlyFields 全局 (...)
  • The DefaultIgnoreCondition global option lets you ignore all value type properties that have default values, or ignore all reference type properties that have null values. DefaultIgnoreCondition 全局选项允许您忽略所有具有默认值的值类型属性,或忽略所有具有空值的引用类型属性。

These options don't let you:这些选项不允许您:

  • Ignore selected properties based on arbitrary criteria evaluated at run time.忽略基于运行时评估的任意标准的选定属性。

For that functionality, you can write a custom converter.对于该功能,您可以编写自定义转换器。 Here's a sample POCO and a custom converter for it that illustrates this approach:这是一个示例 POCO 和一个自定义转换器,用于说明这种方法:

(An example of a custom converter for a type follows) (类型的自定义转换器示例如下)

Please note that this converter needs to be a converter for the type that contains the property that is a collection, it's not a converter for the collection type (for that see my 2nd answer).请注意,此转换器必须是包含集合属性的类型的转换器,它不是集合类型的转换器(请参阅我的第二个答案)。

An attempt一次尝试

I know that this is not what you're after but maybe someone could build on this or there is a very very slim chance that it may suit some scenarios.我知道这不是你想要的,但也许有人可以以此为基础,或者它可能适合某些场景的可能性非常小。

What I managed to do我设法做到的

An object new A() of一个对象new A()

public class A
{
   public List<int> NullList {get;set;}
   public List<int> EmptyList {get;set;} = new List<int>();
};

becomes变成

{
  "EmptyList": null
}

Documentation文档

How to write custom converters for JSON serialization (marshalling) in .NET from 2021 Feb 25 in Custom converter patterns says: How to write custom converters for JSON serialization (marshalling) in .NET from 2021 Feb 25 in Custom converter patterns说:

There are two patterns for creating a custom converter: the basic pattern and the factory pattern.创建自定义转换器有两种模式:基本模式和工厂模式。 The factory pattern is for converters that handle type Enum or open generics.工厂模式适用于处理 Enum 类型或开放泛型的转换器。 The basic pattern is for non-generic and closed generic types.基本模式适用于非泛型和封闭泛型类型。 For example, converters for the following types require the factory pattern:例如,以下类型的转换器需要工厂模式:

  • Dictionary<TKey,TValue>字典<TKey,TValue>
  • Enum枚举
  • List列表

Some examples of types that can be handled by the basic pattern include:可以由基本模式处理的类型的一些示例包括:

  • Dictionary<int, string>字典<整数,字符串>
  • WeekdaysEnum工作日枚举
  • List列表
  • DateTime约会时间
  • Int32整数32

The basic pattern creates a class that can handle one type.基本模式创建了一个可以处理一种类型的类。 The factory pattern creates a class that determines, at run time, which specific type is required and dynamically creates the appropriate converter.工厂模式创建一个类,该类在运行时确定需要哪种特定类型并动态创建适当的转换器。

What I did我做了什么

Demo演示

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;

#nullable disable

namespace Sandbox4
{
 
    public class A
    {
        public List<int> NullList {get;set;}
        public List<int> EmptyList {get;set;} = new List<int>();
     };

    public class Program
    {
        public static void Main()
        {
            A a = new ();

            JsonSerializerOptions options = new ()
            {
                DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault,
                WriteIndented = true,
                Converters =
                {  
                     new IEnumerableTConverter()
                }
            };

            string aJson =
                JsonSerializer.Serialize<A>(a, options);
            
            Console.WriteLine(aJson);
        }
    }
}

Converter转换器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Sandbox4
{
    // Modified DictionaryTEnumTValueConverter 
    // from https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0#custom-converter-patterns
    public class IEnumerableTConverter : JsonConverterFactory
    {
        public override bool CanConvert(Type typeToConvert)
        {
            if (!typeToConvert.IsGenericType)
            {
                return false;
            }

            var realType = typeToConvert.GetGenericTypeDefinition();
            if (realType.IsAssignableTo(typeof(IEnumerable<>)))
            {
                return false;
            }
            return true;
        }

        public override JsonConverter CreateConverter(
            Type type,
            JsonSerializerOptions options)
        {
            Type generictype = type.GetGenericArguments()[0];

            JsonConverter converter = (JsonConverter)Activator.CreateInstance(
                typeof(ICollectionTConverterInner<,>).MakeGenericType(
                    new Type[] { type, generictype }),
                BindingFlags.Instance | BindingFlags.Public,
                binder: null,
                args: new object[] { type, options },
                culture: null);

            return converter;
        }

        private class ICollectionTConverterInner<T,U> :
            JsonConverter<T> where T: IEnumerable<U>
        {
            private readonly JsonConverter<T> _normalConverter;

            public ICollectionTConverterInner(Type type,JsonSerializerOptions options)
            {
                // For performance, use the existing converter if available.
                var existing = new JsonSerializerOptions().GetConverter(type);
                if( existing == null ) throw new ApplicationException($"Standard converter for {type} not found.");

                _normalConverter = (JsonConverter<T>) existing;
            }

            public override T Read(
                ref Utf8JsonReader reader,
                Type typeToConvert,
                JsonSerializerOptions options)
            {
                // Untested
                return _normalConverter.Read(ref reader, typeToConvert, options);
            }

            public override void Write(
                Utf8JsonWriter writer,
                T collection,
                JsonSerializerOptions options)
            {
                if(!collection.Any()) 
                {
                    writer.WriteNullValue();
                    return;
                }

                _normalConverter.Write(writer, collection, options);
            }
        }
    }
}

TypeInfoResolver added in .NET 7(accessible in older runtimes via the system.text.json nuget package, pre-release as of time of this answer) allows this. TypeInfoResolver added in .NET 7(accessible in older runtimes via the system.text.json nuget package, pre-release as of time of this answer) allows this.

Example:例子:

using System.Collections;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;

public class TestObject {
    public List<int> Ints { get; } = new() {3, 4, 5};
    public List<int> EmptyInts { get; } = new();
    public List<int> NullInts { get; }
}

public static class Program {
    public static void Main() {
        var options = new JsonSerializerOptions {
            TypeInfoResolver = new DefaultJsonTypeInfoResolver {
                Modifiers = {DefaultValueModifier}
            },
        };
        var obj = new TestObject();
        var text = JsonSerializer.Serialize(obj, options);
        Console.WriteLine(text);
    }

    private static void DefaultValueModifier(JsonTypeInfo type_info) {
        foreach (var property in type_info.Properties) {
            if (typeof(ICollection).IsAssignableFrom(property.PropertyType)) {
                property.ShouldSerialize = (_, val) => val is ICollection collection && collection.Count > 0;
            }
        }
    }
}

output: output:

{"Ints":[3,4,5]}

暂无
暂无

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

相关问题 System.Text.Json access Json object of json object using c# .net core 3.1 - System.Text.Json access Json object of json object using c# .net core 3.1 C# System.Text.Json 在序列化期间“rounds”加倍 - C# System.Text.Json "rounds" doubles during serialization c#/.net core3.0 System.Text.Json - JsonSerializer.SerializeAsync? - c#/.net core3.0 System.Text.Json - JsonSerializer.SerializeAsync? .NET CORE 3.1 ApiController 装饰器是否使用 System.Text.Json 返回 json? - .NET CORE 3.1 Does ApiController decorator return json using System.Text.Json? 使用 C# System.Text.Json 对带有混合大小写的 json 文本进行序列化/反序列化 - Serialization/deserialization of json text with mix casing for property names using C# System.Text.Json 如何在.net core 3.1中使用System.Text.Json获取对象内部对象的值 - How to get value of object inside object using System.Text.Json in .net core 3.1 ASP.NET Core 3.0 System.Text.Json Camel Case连载 - ASP.NET Core 3.0 System.Text.Json Camel Case Serialization 使用 .NET Core 3.1 中的 System.Text.Json 反序列化会导致 null 属性值 - Deserialization with System.Text.Json in .NET Core 3.1 results in null property values C# System.Text.Json 或 Newtonsoft JSON 属性标记,用于按属性进行序列化控制 - C# System.Text.Json or Newtonsoft JSON properties marking for serialization control by attributes C# .Net Core - System.Text.Json - 是否可以为每个 JSON 段使用多个 JsonConverters 反序列化对象图? - C# .Net Core - System.Text.Json - Is it possible to Deserialize an object graph using many JsonConverters for each JSON segment?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM