50

Am looking to Serialize a list using NewtonSoft JSON and i need to ignore one of the property while Serializing and i got the below code

public class Car
{
  // included in JSON
  public string Model { get; set; }
  // ignored
  [JsonIgnore]
  public DateTime LastModified { get; set; }
}

But am using this Specific class Car in many places in my application and i want to Exclude the option only in one place.

Can i dynamically add [JsonIgnore] in the Specific Place where i need ? How do i do that ?

Peru
  • 2,871
  • 5
  • 37
  • 66

7 Answers7

100

No need to do the complicated stuff explained in the other answer.

NewtonSoft JSON has a built-in feature for that:

public bool ShouldSerializeINSERT_YOUR_PROPERTY_NAME_HERE()
{
    if(someCondition){
        return true;
    }else{
        return false;
    }
}

It is called "conditional property serialization" and the documentation can be found here.

Warning: first of all, it is important to get rid of [JsonIgnore] above your {get;set;} property. Otherwise it will overwrite the ShouldSerializeXYZ behavior.

Xavier Peña
  • 7,399
  • 9
  • 57
  • 99
  • 3
    this should really get flagged as the answer. – Trent May 01 '17 at 19:14
  • 5
    This only works if you can calculate the condition value inside the model class, which is not very useful in cases, where you'd have to specify ignore policy outside the model, i.e. in specific API method that doesn't need to return all the data. – fingust Apr 09 '20 at 08:37
  • 2
    @fingust Couldn't you have a property on the class, which contains a list of properties not to serialize.Then lock and set values in this property prior to calling the serialisation, and clear it post-serialisation to make it run-time manageable? – hynsey May 23 '20 at 14:45
  • A very elegant solution. Thanks. – Muzaffer Galata Jul 06 '22 at 10:41
23

I think it would be best to use a custom IContractResolver to achieve this:

public class DynamicContractResolver : DefaultContractResolver
{
    private readonly string _propertyNameToExclude;

    public DynamicContractResolver(string propertyNameToExclude)
    {
        _propertyNameToExclude = propertyNameToExclude;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);

        // only serializer properties that are not named after the specified property.
        properties =
            properties.Where(p => string.Compare(p.PropertyName, _propertyNameToExclude, true) != 0).ToList();

        return properties;
    }
}

The LINQ may not be correct, I haven't had a chance to test this. You can then use it as follows:

string json = JsonConvert.SerializeObject(car, Formatting.Indented,
   new JsonSerializerSettings { ContractResolver = new DynamicContractResolver("LastModified") });

Refer to the documentation for more information.

Underscore
  • 1,017
  • 2
  • 10
  • 26
  • Based on this post, I created a list of properties to exclude on serialization. Check it on my feedback above. – diegodsp Aug 22 '17 at 11:31
  • A better way that does not allocate is just to set ignored = true for the property – Anders Feb 25 '19 at 14:05
14

Based on @Underscore post above, I created a list of properties to exclude on serialization.

public class DynamicContractResolver : DefaultContractResolver {
    private readonly string[] props;

    public DynamicContractResolver(params string[] prop) {
        this.props = prop;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) {
        IList<JsonProperty> retval = base.CreateProperties(type, memberSerialization);

        // return all the properties which are not in the ignore list
        retval = retval.Where(p => !this.props.Contains(p.PropertyName)).ToList();

        return retval;
    }
}

Use:

string json = JsonConvert.SerializeObject(car, Formatting.Indented, 
    new JsonSerializerSettings { ContractResolver = new DynamicContractResolver("ID", "CreatedAt", "LastModified") });
adiga
  • 34,372
  • 9
  • 61
  • 83
diegodsp
  • 882
  • 8
  • 12
6

With the reference Dynamically rename or ignore properties without changing the serialized class we can achieve JsonIgnore at run time. It's a workable solution.

Consider Person Class for example:

public class Person
{
    // ignore property
    [JsonIgnore]
    public string Title { get; set; }

// rename property
[JsonProperty("firstName")]
public string FirstName { get; set; }
}

Step 1: Create Class "PropertyRenameAndIgnoreSerializerContractResolver"

public class PropertyRenameAndIgnoreSerializerContractResolver : DefaultContractResolver
{
    private readonly Dictionary<Type, HashSet<string>> _ignores;
    private readonly Dictionary<Type, Dictionary<string, string>> _renames;

public PropertyRenameAndIgnoreSerializerContractResolver()
{
    _ignores = new Dictionary<Type, HashSet<string>>();
    _renames = new Dictionary<Type, Dictionary<string, string>>();
}

public void IgnoreProperty(Type type, params string[] jsonPropertyNames)
{
    if (!_ignores.ContainsKey(type))
        _ignores[type] = new HashSet<string>();

    foreach (var prop in jsonPropertyNames)
        _ignores[type].Add(prop);
}

public void RenameProperty(Type type, string propertyName, string newJsonPropertyName)
{
    if (!_renames.ContainsKey(type))
        _renames[type] = new Dictionary<string, string>();

    _renames[type][propertyName] = newJsonPropertyName;
}

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

    if (IsIgnored(property.DeclaringType, property.PropertyName))
    {
        property.ShouldSerialize = i => false;
        property.Ignored = true;
    }

    if (IsRenamed(property.DeclaringType, property.PropertyName, out var newJsonPropertyName))
        property.PropertyName = newJsonPropertyName;

    return property;
}

private bool IsIgnored(Type type, string jsonPropertyName)
{
    if (!_ignores.ContainsKey(type))
        return false;

    return _ignores[type].Contains(jsonPropertyName);
}

private bool IsRenamed(Type type, string jsonPropertyName, out string newJsonPropertyName)
{
    Dictionary<string, string> renames;

    if (!_renames.TryGetValue(type, out renames) || !renames.TryGetValue(jsonPropertyName, out newJsonPropertyName))
    {
        newJsonPropertyName = null;
        return false;
    }

    return true;
}
}

Step 2: Add code in your method where Jsonignore want to apply

var person = new Person();
var jsonResolver = new PropertyRenameAndIgnoreSerializerContractResolver();

jsonResolver.IgnoreProperty(typeof(Person), "Title");
jsonResolver.RenameProperty(typeof(Person), "FirstName", "firstName");

var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = jsonResolver;

var json = JsonConvert.SerializeObject(person, serializerSettings);
Rahul Modi
  • 748
  • 11
  • 21
1

With respect to all correct answers I would like to add something. When you have nested properties with the same name so ignoring will effect on all properties with the same name. If you like to ignore a specific property you can do something like this:

    public class DynamicContractResolver : DefaultContractResolver
    {
        private readonly string[] props;

        public DynamicContractResolver(params string[] prop)
        {
           this.props = prop;
        }

        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
           IList<JsonProperty> retval = base.CreateProperties(type, memberSerialization);
           return retval.Where(p => !this.props.Contains(p.DeclaringType.FullName + "." + p.PropertyName)).ToList();
        }
    }

then when you want to use it you can say:

var values = await _dbContext
                .Set<EntityName>()
                .Where(...).ToList();


            var json = JsonConvert.SerializeObject(values, Formatting.Indented,
                    new JsonSerializerSettings
                    {
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                        ContractResolver = new DynamicContractResolver("Entities.Contact.Address1","Entities.User.Name","Entities.Event.Name")
                    });

The Address1 will be ignored in Contact not anywhere else.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Ali Mahmoodi
  • 858
  • 10
  • 14
0

Try this:

    public static void IgnoreProperty<T, TR>(this T parameter, Expression<Func<T, TR>> propertyLambda)
    {
        var parameterType = parameter.GetType();
        var propertyName = propertyLambda.GetReturnedPropertyName();
        if (propertyName == null)
        {
            return;
        }

        var jsonPropertyAttribute = parameterType.GetProperty(propertyName).GetCustomAttribute<JsonPropertyAttribute>();
        jsonPropertyAttribute.DefaultValueHandling = DefaultValueHandling.Ignore;
    }

    public static string GetReturnedPropertyName<T, TR>(this Expression<Func<T, TR>> propertyLambda)
    {
        var member = propertyLambda.Body as MemberExpression;
        var memberPropertyInfo = member?.Member as PropertyInfo;
        return memberPropertyInfo?.Name;
    }

So you can do this:

carObject.IgnoreProperty(so => so.LastModified);
  • where is GetReturnedPropertyName() defined? – Outside the Box Developer May 06 '17 at 22:13
  • This is kind of useless in that you must already have added a `JsonPropertyAttribute` on the properties you want to set to ignore. In which case, you might as well just set the `DefaultValueHandling ` directly. If you haven't, `GetCustomAttribute()` returns null, and you get a `NullReferenceException` on the following line. – Alex Jan 10 '19 at 04:03
0

Based on the accepted answer it would be like:

[JsonIgnore]
public bool JsonIgnore { get; set; }

public bool ImageModified { get; set; }

public bool ShouldSerializeImageModified() => !JsonIgnore;

Whenever JsonIgnore is set to true it means that ImageModified won't be serialized, and JsonIgnore is ignored because of [JsonIgnore].

If there is a need to write code this way, it might be an indication of poor design. Probably there needs to be a DTO or ViewModel in the system, unless you want to dynamically disable/enable serialization of some properties.

Saber
  • 5,150
  • 4
  • 31
  • 43