如何在父类中使用JsonIgnore属性忽略一个属性

huangapple go评论75阅读模式
英文:

How to ignore a property with JsonIgnore attribute in parent class

问题

我在父类中使用了JsonIgnore特性,但序列化的子类仍然具有此属性。

public class Parent
{
      [JsonIgnore]
      public virtual string ParentProperty { get; set; }
}

public class Child : Parent
{
      public override string ParentProperty { get; set; } = "ParentProperty";
        
      public string ChildProperty { get; set; }
}

var child = new Child
{
      ParentProperty = "ParentProperty",
      ChildProperty = "ChildProperty"
};

Console.WriteLine(JsonSerializer.Serialize(child)); 
// {"ParentProperty":"ParentProperty","ChildProperty":"ChildProperty"}

我想在序列化超类时忽略此属性,但我不想为每个子类都添加JsonIgnore特性。我该怎么办?

英文:

I use the JsonIgnore feature in the parent class, but the serialized subclass still has this property.

public class Parent
{
      [JsonIgnore]
      public virtual string ParentProperty { get; set; }
}

public class Child : Parent
{
      public override string ParentProperty { get; set; } = "ParentProperty";
        
      public string ChildProperty { get; set; }
}

var child = new Child
{
      ParentProperty = "ParentProperty",
      ChildProperty = "ChildProperty"
};

Console.WriteLine(JsonSerializer.Serialize(child)); 
// {"ParentProperty":"ParentProperty","ChildProperty":"ChildProperty"}

I want to ignore this property for the superclass when serializing, but I don't want to add the JsonIgnore feature to every subclass.
What should i do?

答案1

得分: 1

这是System.Text.Json已知的错误或限制:

Microsoft建议的解决方法是在子属性上标记与父属性相同的[JsonIgnore]属性:

public class Parent
{
    [JsonIgnore] public virtual string ParentProperty { get; set; }
}

public class Child : Parent
{
    [JsonIgnore] public override string ParentProperty { get; set; }
    public string ChildProperty { get; set; }
}

演示示例 #1 在此处

或者,在.NET 7及更高版本中,您可以使用合同定制来添加一个修改器,将基本属性的JsonIgnoreAttribute应用于继承的属性:

public static partial class JsonExtensions
{
    public static Action<JsonTypeInfo> InheritJsonIgnoreAttributes { get; } = 
        static typeInfo => 
        {
            if (typeInfo.Kind != JsonTypeInfoKind.Object)
                return;
            foreach (var property in typeInfo.Properties)
            {
                if (property.ShouldSerialize != null)
                    continue;
                var attr = property.GetPropertyInfo()?.GetCustomAttribute<JsonIgnoreAttribute>(true);
                if (attr != null)
                {
                    var shouldSerialize = attr.Condition switch
                    {
                        JsonIgnoreCondition.Always => static (_, _) => false,
                        JsonIgnoreCondition.WhenWritingNull => static (_, v) => v != null,
                        JsonIgnoreCondition.WhenWritingDefault => !(property.PropertyType.IsValueType || Nullable.GetUnderlyingType(property.PropertyType) != null)
                            ? static (_, v) => v != null
                            : ((IShouldSerializeProvider)Activator.CreateInstance(typeof(DefaultIgnorer<>).MakeGenericType(property.PropertyType))!).ShouldSerialize,
                        JsonIgnoreCondition.Never => static (_, v) => true,
                        _ => null,
                    };
                    if (shouldSerialize != null)
                        property.ShouldSerialize = shouldSerialize;
                }
            }
        };
    
    public static PropertyInfo? GetPropertyInfo(this JsonPropertyInfo property) => (property.AttributeProvider as PropertyInfo);
    
    interface IShouldSerializeProvider { Func<object, object?, bool> ShouldSerialize { get; } }
    
    class DefaultIgnorer<T> : IShouldSerializeProvider
    {
        readonly IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
        public Func<object, object?, bool> ShouldSerialize { get => (obj, value) => value == null ? false : !comparer.Equals(default(T), (T)value); }
    }
}

然后进行序列化:

var options = new JsonSerializerOptions
{
    TypeInfoResolver = new DefaultJsonTypeInfoResolver
    {
        Modifiers = { JsonExtensions.InheritJsonIgnoreAttributes },
    },
};

var json = JsonSerializer.Serialize(child, options);

演示示例 #2 在此处

英文:

This is a known bug or limitation with System.Text.Json:

The workaround suggested by Microsoft is to mark the child property with the same [JsonIgnore] attribute as the parent property:

public class Parent
{
	[JsonIgnore] public virtual string ParentProperty { get; set; }
}

public class Child : Parent
{
	[JsonIgnore] public override string ParentProperty { get; set; }
	public string ChildProperty { get; set; }
}

Demo fiddle #1 here.

Alternatively, in .NET 7 and later, you can use contract customization to add a modifier that applies the JsonIgnoreAttribute of base properties to inherited properties:

public static partial class JsonExtensions
{
	public static Action&lt;JsonTypeInfo&gt; InheritJsonIgnoreAttributes { get; } = 
		static typeInfo =&gt; 
		{
			if (typeInfo.Kind != JsonTypeInfoKind.Object)
				return;
			foreach (var property in typeInfo.Properties)
			{
				if (property.ShouldSerialize != null)
					continue;
				var attr = property.GetPropertyInfo()?.GetCustomAttribute&lt;JsonIgnoreAttribute&gt;(true);
				if (attr != null)
				{
					var shouldSerialize = attr.Condition switch
					{
						JsonIgnoreCondition.Always =&gt; static (_, _) =&gt; false,
						JsonIgnoreCondition.WhenWritingNull =&gt; static (_, v) =&gt; v != null,
						JsonIgnoreCondition.WhenWritingDefault =&gt; !(property.PropertyType.IsValueType || Nullable.GetUnderlyingType(property.PropertyType) != null)
							? static (_, v) =&gt; v != null
							: ((IShouldSerializeProvider)Activator.CreateInstance(typeof(DefaultIgnorer&lt;&gt;).MakeGenericType(property.PropertyType))!).ShouldSerialize,
						JsonIgnoreCondition.Never =&gt; static (_, v) =&gt; true,
						_ =&gt; null,
					};
					if (shouldSerialize != null)
						property.ShouldSerialize = shouldSerialize;
				}
			}
		};
	
	public static PropertyInfo? GetPropertyInfo(this JsonPropertyInfo property) =&gt; (property.AttributeProvider as PropertyInfo);
	
	interface IShouldSerializeProvider { Func&lt;object, object?, bool&gt; ShouldSerialize { get; } }
	
	class DefaultIgnorer&lt;T&gt; : IShouldSerializeProvider
	{
		readonly IEqualityComparer&lt;T&gt; comparer = EqualityComparer&lt;T&gt;.Default;
		public Func&lt;object, object?, bool&gt; ShouldSerialize { get =&gt; (obj, value) =&gt; value == null ? false : !comparer.Equals(default(T), (T)value); }
	}
}

Then serialize as follows:

var options = new JsonSerializerOptions
{
	TypeInfoResolver = new DefaultJsonTypeInfoResolver
	{
		Modifiers = { JsonExtensions.InheritJsonIgnoreAttributes },
	},
};

var json = JsonSerializer.Serialize(child, options);

Demo fiddle #2 here.

huangapple
  • 本文由 发表于 2023年3月7日 22:52:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75663555.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定