24

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 EmailAddress { get; set; }

    [JsonIgnore]
    public string PasswordHash { get; set; }
}

How can I make Json.NET ignore specific properties when serializing a 3rd-party object with JsonConvert.SerializeObject?

Thomas Johnson
  • 10,776
  • 18
  • 60
  • 98
  • Have a look at [IContractResolver](http://james.newtonking.com/json/help/index.html?topic=html/ConditionalProperties.htm) – Guillaume CR Sep 09 '14 at 16:36

2 Answers2

40

Make a custom contract resolver:

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

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);        
        if (typeof(Account).IsAssignableFrom(member.DeclaringType) && member.Name == nameof(Account.PasswordHash))
        {
            property.Ignored = true;
        }
        return property;
    }
}

How I test it:

        var account = new Account
        {
            PasswordHash = "XXAABB"
        };
        var settings = new JsonSerializerSettings
        {
            ContractResolver = ShouldSerializeContractResolver.Instance
        };
        var json = JsonConvert.SerializeObject(account, settings);
        Console.WriteLine(json);
tia
  • 9,518
  • 1
  • 30
  • 44
  • 2
    Small optimization: `if (!property.Ignored && property.DeclaringType == typeof(Account) && property.PropertyName == "PasswordHash")` – Lorenzo Solano Martinez Oct 02 '17 at 20:39
  • That's trivial and arguably slower because IContractResolver is shared among all classes and most properties are not ignored, so most of the times we waste time checking `!property.Ignored`. The contract is also created lazily once per class so I think it is better to keep it simple. – tia Oct 03 '17 at 15:18
  • Does this also work conditionally based on the value of the serialised instance? I can't find the member's value anywhere. – ygoe Nov 14 '18 at 20:40
  • 1
    Nevermind, I've found it out. Don't set `property.Ignore` but `property.ShouldSerialize`. That can process each value. – ygoe Nov 14 '18 at 21:51
2

Luckily, Newtonsoft.Json has an override on the JsonConvert.SerializeObject() method that allows us to provide a type, so that the resulting JSON doesn't contain properties that don't exist in that type. So, to eliminate properties, you can make a safe copy of your Account class with all the sensitive properties removed, and give it a different name:

public class AccountJSON
{
    public string FullName { get; set; }
    public string EmailAddress { get; set; }
}

Provide its type when serializing:

var TheAccount = DBContext.Accounts.Find(1);
var TheJSON = Newtonsoft.Json.JsonConvert.SerializeObject(TheAccount, typeof(AccountJSON));

Note: This may only work on the first level deep when the serializer travels through the object. If the Account object has lazy loading properties that reference even more Account objects, they may not use the "safe" type that you originally provided.

user3163495
  • 2,425
  • 2
  • 26
  • 43