I have an object that looks something like this (obviously simplified)
public class Person {
  public string Name { get; set; }
  public int Age { get; set; }
  public string ETag { get { return ... } }
}
What I would like is for ETag to be a hash of the json serialization of the object omitting the ETag property (to prevent a recursive loop). However, I cannot just use a [JsonIgnore] attribute since at other times I want to be able to json serialize the entire thing.
So what I want is something like this
public string ETag { get {
   return Hash(JsonConvert.Serialize(this, p => p.Ignore(x=>x.ETag) ));
}}
which is unfortunately not an API that exists. How would I achieve something similar?
You can use a custom IContractResolver to programmatically ignore properties on an object.  So I think the approach I would take is to create a simple resolver that can specifically ignore a single property on a single type (obviously you could extend this if needed), then make a helper method that can serialize using that resolver.  Use the helper method from within your ETag property and you're good to go.
Here's the code for the resolver:
class IgnorePropertyResolver : DefaultContractResolver
{
    Type targetType;
    string targetPropertyName;
    public IgnorePropertyResolver(Type targetType, string propertyName)
    {
        this.targetType = targetType;
        this.targetPropertyName = propertyName;
    }
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
        if (targetType == type)
        {
            props = props.Where(p => p.PropertyName != targetPropertyName).ToList();
        }
        return props;
    }
}
Here's the helper method (I also threw a Hash helper method in there since you had not defined it in your question):
static class JsonHelper
{
    public static string Serialize(object target, string propertyToIgnore)
    {
        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.ContractResolver = new IgnorePropertyResolver(target.GetType(), propertyToIgnore);
        return JsonConvert.SerializeObject(target, settings);
    }
    public static string Hash(string json)
    {
        using (var sha = new SHA1Managed())
        {
            return Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(json)));
        }
    }
}
And finally, here's a working demo:
class Program
{
    static void Main(string[] args)
    {
        Person p = new Person { Name = "Joe", Age = 26 };
        Console.WriteLine("Etag = " + p.ETag);
        Console.WriteLine();
        Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented));
    }
}
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string ETag
    {
        get { return JsonHelper.Hash(JsonHelper.Serialize(this, "ETag")); }
    }
}
Output:
Etag = T99YVDlrbZ66YL2u5MYjyIyO4Qk=
{
  "Name": "Joe",
  "Age": 26,
  "ETag": "T99YVDlrbZ66YL2u5MYjyIyO4Qk="
}
Fiddle: https://dotnetfiddle.net/YgVJ4K
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With