Exclude property from serialization via custom attribute (json.net)

Rex M picture Rex M · Nov 27, 2012 · Viewed 69.9k times · Source

I need to be able to control how/whether certain properties on a class are serialized. The simplest case is [ScriptIgnore]. However, I only want these attributes to be honored for this one specific serialization situation I am working on - if other modules downstream in the application also want to serialize these objects, none of these attributes should get in the way.

So my thought is to use a custom attribute MyAttribute on the properties, and initialize the specific instance of JsonSerializer with a hook that knows to look for that attribute.

At first glance, I don't see any of the available hook points in JSON.NET will provide the PropertyInfo for the current property to do such an inspection - only the property's value. Am I missing something? Or a better way to approach this?

Answer

drzaus picture drzaus · Jan 24, 2013

Here's a generic reusable "ignore property" resolver based on the accepted answer:

/// <summary>
/// Special JsonConvert resolver that allows you to ignore properties.  See https://stackoverflow.com/a/13588192/1037948
/// </summary>
public class IgnorableSerializerContractResolver : DefaultContractResolver {
    protected readonly Dictionary<Type, HashSet<string>> Ignores;

    public IgnorableSerializerContractResolver() {
        this.Ignores = new Dictionary<Type, HashSet<string>>();
    }

    /// <summary>
    /// Explicitly ignore the given property(s) for the given type
    /// </summary>
    /// <param name="type"></param>
    /// <param name="propertyName">one or more properties to ignore.  Leave empty to ignore the type entirely.</param>
    public void Ignore(Type type, params string[] propertyName) {
        // start bucket if DNE
        if (!this.Ignores.ContainsKey(type)) this.Ignores[type] = new HashSet<string>();

        foreach (var prop in propertyName) {
            this.Ignores[type].Add(prop);
        }
    }

    /// <summary>
    /// Is the given property for the given type ignored?
    /// </summary>
    /// <param name="type"></param>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool IsIgnored(Type type, string propertyName) {
        if (!this.Ignores.ContainsKey(type)) return false;

        // if no properties provided, ignore the type entirely
        if (this.Ignores[type].Count == 0) return true;

        return this.Ignores[type].Contains(propertyName);
    }

    /// <summary>
    /// The decision logic goes here
    /// </summary>
    /// <param name="member"></param>
    /// <param name="memberSerialization"></param>
    /// <returns></returns>
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (this.IsIgnored(property.DeclaringType, property.PropertyName)
        // need to check basetype as well for EF -- @per comment by user576838
        || this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName)) {
            property.ShouldSerialize = instance => { return false; };
        }

        return property;
    }
}

And usage:

var jsonResolver = new IgnorableSerializerContractResolver();
// ignore single property
jsonResolver.Ignore(typeof(Company), "WebSites");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };