英文:
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<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); }
}
}
Then serialize as follows:
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { JsonExtensions.InheritJsonIgnoreAttributes },
},
};
var json = JsonSerializer.Serialize(child, options);
Demo fiddle #2 here.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论