0

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?

Hobo
  • 41
  • 1
  • 5
  • You will need to write a specific JsonConverter to do this – stuartd Mar 07 '23 at 15:38
  • Not sure why this was closed, after the code was added the question is perfectly clear and answerable, and relates to open System.Text.Json issue [JsonIgnore attribute is not inherited in overridden properties #50078](https://github.com/dotnet/runtime/issues/50078). – dbc Mar 09 '23 at 03:51

1 Answers1

1

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.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • 1
    Thanks for your answer, it was not practical for me to write another Converter, so I decided to fix the problem by adding the JsonIgnore attribute to the subclass as well. – Hobo Mar 08 '23 at 00:53