Ignore property defined in interface when serializing using JSON.net

In more recent versions of Json.NET, applying [JsonIgnore] to interface properties now just works and successfully prevents them from being serialized for all implementing types, as long as the property is declared on the same class where the interface is declared. A custom contract resolver is no longer required.

For instance, if we define the following types:

public interface IFoo 
{
    [JsonIgnore]
    string SecretProperty  { get; set; }

    string Include { get; set; }
}

public class Foo : IFoo 
{
    public string SecretProperty  { get; set; }
    public string Include { get; set; }
}

Then the following test passes in Json.NET 11 and 12 (and probably earlier versions also):

var root = new Foo
{
    SecretProperty  = "Ignore Me",
    Include = "Include Me",
};

var json = JsonConvert.SerializeObject(root);

Assert.IsTrue(json == "{\"Include\":\"Include Me\"}");// Passes

Demo fiddles here and here.

I believe this was added in Json.NET 4.0.3 despite the fact that JsonIgnore was not mentioned explicitly in the release notes:

New feature - JsonObject and JsonProperty attributes can now be placed on an interface and used when serializing implementing objects.

(The implementation can be found in JsonTypeReflector.GetAttribute<T>(MemberInfo memberInfo).)

However, as noted by Vitaly, this does not work when the property is inherited from a base class of the class where the interface is declared. Demo fiddle here.


I have found it's simplest to create a DTO of only the properties I want and serialize that object to JSON. it creates many small, context specific objects but managing the code base is much easier and I don't have to think about what I'm serializing vs what I'm ignoring.


After a bit of searching, I found this question:

How to inherit the attribute from interface to object when serializing it using JSON.NET

I took the code by Jeff Sternal and added JsonIgnoreAttribute detection, so it looks like this:

class InterfaceContractResolver : DefaultContractResolver
{
    public InterfaceContractResolver() : this(false) { }

    public InterfaceContractResolver(bool shareCache) : base(shareCache) { }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        var interfaces = member.DeclaringType.GetInterfaces();
        foreach (var @interface in interfaces)
        {
            foreach (var interfaceProperty in @interface.GetProperties())
            {
                // This is weak: among other things, an implementation 
                // may be deliberately hiding an interface member
                if (interfaceProperty.Name == member.Name && interfaceProperty.MemberType == member.MemberType)
                {
                    if (interfaceProperty.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Any())
                    {
                        property.Ignored = true;
                        return property;
                    }

                    if (interfaceProperty.GetCustomAttributes(typeof(JsonPropertyAttribute), true).Any())
                    {
                        property.Ignored = false;
                        return property;
                    }
                }
            }
        }

        return property;
    }
}

Using this InterfaceContractResolver in my JsonSerializerSettings, all properties which have a JsonIgnoreAttribute in any interface are ignored, too, even if they have a JsonPropertyAttribute (due to the order of the inner if blocks).

Tags:

C#

.Net

Json.Net