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

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?

You have a few options. I recommend you read the Json.Net documentation article on the subject before reading below.

The article presents two methods:

  1. Create a method that returns a bool value based on a naming convention that Json.Net will follow to determine whether or not to serialize the property.
  2. Create a custom contract resolver that ignores the property.

Of the two, I favor the latter. Skip attributes altogether -- only use them to ignore properties across all forms of serialization. Instead, create a custom contract resolver that ignores the property in question, and only use the contract resolver when you want to ignore the property, leaving other users of the class free to serialize the property or not at their own whim.

Edit To avoid link rot, I'm posting the code in question from the article

public class ShouldSerializeContractResolver : DefaultContractResolver
{
   public new static readonly ShouldSerializeContractResolver Instance =
                                 new ShouldSerializeContractResolver();

   protected override JsonProperty CreateProperty( MemberInfo member,
                                    MemberSerialization memberSerialization )
   {
      JsonProperty property = base.CreateProperty( member, memberSerialization );

      if( property.DeclaringType == typeof(Employee) &&
            property.PropertyName == "Manager" )
      {
         property.ShouldSerialize = instance =>
         {
            // replace this logic with your own, probably just  
            // return false;
            Employee e = (Employee)instance;
            return e.Manager != e;
         };
      }

      return property;
   }
}

JsonIgnoreAttribute, This sample uses the JsonIgnoreAttribute to exclude a property from serialization. Sample. Types. Copy. public class Account { public string FullName { get; set; }  Configure NamingStrategy dictionary serialization Configure NamingStrategy property name serialization This sample uses the JsonIgnoreAttribute to exclude a property from serialization.

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 };

Conditional Property Serialization, Json.NET has the ability to conditionally serialize properties by placing a ShouldSerialize method on a class. This functionality is similar to the XmlSerializer  Json.Net custom serialization from attribute. with an attribute to tell Json.Net to use the custom converter ignore a property in class if null, using json

Use the JsonIgnore attribute.

For example, to exclude Id:

public class Person {
    [JsonIgnore]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Advanced Newtonsoft.Json: Dynamically rename or ignore , Json: Dynamically rename or ignore properties without changing the serialized class a custom contract resolver, so that you can ignore or rename serialized JSON I had to ignore or rename properties in the Swagger serializer. Please enable JavaScript to view the comments powered by Disqus. Every custom type can opt how it will be serialized. To example, mark the type with [JsonObject(MemberSerialization = MemberSerialization.OptIn)] and then you have to mark something with [JsonProperty] otherwise nothing will be serialized. So even if property of custom type is serializable the type may produce nothing ({}) to serialize:

Here is a method based on drzaus' excellent serializer contract which uses lambda expressions. Simply add it to the same class. After all, who doesn't prefer the compiler to do the checking for them?

public IgnorableSerializerContractResolver Ignore<TModel>(Expression<Func<TModel, object>> selector)
{
    MemberExpression body = selector.Body as MemberExpression;

    if (body == null)
    {
        UnaryExpression ubody = (UnaryExpression)selector.Body;
        body = ubody.Operand as MemberExpression;

        if (body == null)
        {
            throw new ArgumentException("Could not get property name", "selector");
        }
    }

    string propertyName = body.Member.Name;
    this.Ignore(typeof (TModel), propertyName);
    return this;
}

You can now ignore properties easily and fluently:

contract.Ignore<Node>(node => node.NextNode)
    .Ignore<Node>(node => node.AvailableNodes);

Conditionally Serializing Fields and Properties with Json.NET , When you are serializing an object using Json.NET, all its public fields and properties will be serialized by default. If you want to exclude certain  Rather than introduce a new attribute, we decided to allow more general extensions to serialization behavior via well-known mechanisms in JSON.NET (JsonConverters and IContractResolvers). This kind of customization will be enabled in the next version of the SDK (1.0.0-preview), due to ship in a few weeks.

I don't care to set the property names as strings, in case they ever change it would break my other code.

I had several "view modes" on the objects I needed to serialized, so I ended up doing something like this in the contract resolver (view mode provided by constructor argument):

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
    JsonProperty property = base.CreateProperty(member, memberSerialization);
    if (viewMode == ViewModeEnum.UnregisteredCustomer && member.GetCustomAttributes(typeof(UnregisteredCustomerAttribute), true).Length == 0)
    {
        property.ShouldSerialize = instance => { return false; };
    }

    return property;
}

Where my objects look like this:

public interface IStatement
{
    [UnregisteredCustomer]
    string PolicyNumber { get; set; }

    string PlanCode { get; set; }

    PlanStatus PlanStatus { get; set; }

    [UnregisteredCustomer]
    decimal TotalAmount { get; }

    [UnregisteredCustomer]
    ICollection<IBalance> Balances { get; }

    void SetBalances(IBalance[] balances);
}

The downside to this would be the bit of reflection in the resolver, but I think it's worth it to have more maintainable code.

How to serialize and deserialize JSON using C#, Text.Json namespace to serialize to and deserialize from JSON in .NET. Customize JSON names and values; Exclude properties from serialization; Customize To use a custom JSON property naming policy, create a class that derives from  Json.net does have the ability to conditionally serialize properties without an attribute or contract resolver. This is especially useful if you don't want your project to have a dependency on Json.net. As per the Json.net documentation

Migrate from Newtonsoft.Json to System.Text.Json, The workarounds are custom converters, which may not provide DefaultContractResolver to exclude properties, ⚠️ Not supported, workaround, sample Json can serialize or deserialize numbers represented by JSON  The Json.NET documentation says you use JsonIgnore to not serialize certain properties in your classes: public class Account { public string FullName { get; set; } public string EmailAddre

System.Text.Json: How to serialize only certain properties without , Json and we used to have code that would opt-in to serialization instead of Json: How to serialize only certain properties without using JsonIgnoreAttribute #​593 NET Core from deserializing the HTTP request into an object. I have a custom attribute [SensitiveData] decorating certain properties (SSN,  Similar to @sirthomas's answer, JSON.NET also respects the EmitDefaultValue property on DataMemberAttribute: [DataMember(Name="property_name", EmitDefaultValue=false)] This may be desirable if you are already using [DataContract] and [DataMember] in your model type and don't want to add JSON.NET-specific attributes.

Fine-tune Json Serialization, Serialization process can be fine-tuned: properties can be included and excluded​. There are By using include() and exclude() methods we can include and exclude a property referenced by it's path. An awesome feature is that it is possible to set custom JSON annotation. Previous: Json SerializerNext: Json Parser. The MemberSerialization flag on this attribute specifies whether member serialization is opt-in (a member must have the JsonProperty or DataMember attribute to be serialized), opt-out (everything is serialized by default but can be ignored with the JsonIgnoreAttribute, Json.NET's default behavior) or fields (all public and private fields are serialized and properties are ignored).

Comments
  • Why do they use the new modifier when declaring Instance? DefaultContractResolver does not declare an Instance member. Additionally, what is the purpose of Instance even being declared here?
  • @xr280xr I'm not sure. It's been a few years since this was posted... maybe it used to have an Instance property?
  • Maybe, but they still have it in their example. I was trying to figure out why which is how I ended up here.
  • It seems unnecessary to me. I can’t verify right now but if you’re about to use the example, try removing new and see what happens.
  • I know this has been answered, but I found when seralizing EF models, you need to compare the basetype. EX) if (this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName))
  • @user576838 that is a good point. I think it depends on your flavor (code-first vs database-first) -- I think EF makes "random" proxy classes in the case of database-first, but might not in code-first? Probably safer to include the check though!
  • @user576838 - added extra check for EF
  • In the Ignore method when we start the bucket, it may make sense to declare the HashSet as follows to ignore case for the propertyname Change this if (!Ignores.ContainsKey(type)) Ignores[type] = new HashSet<string>(); To this if (!Ignores.ContainsKey(type)) Ignores[type] = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  • Also the foreach loop in the Ignore method should have a continue if the property is already in the hashset. ` foreach (var prop in propertyName) { if (Ignores[type].Contains(prop)) continue; Ignores[type].Add(prop); }`
  • Can you give an explanation on your answer please?
  • JsonIgnore may work on classes under control, but not 3rd party classes. And even when having out custom classes, sometimes we might need only part of the class to be serialized.
  • I've added an example. I don't think this answer answers the question, but it did assist me with what I was trying to do.
  • This would cause the properties to be ignored in all serialization, not only in the specific one as required.