5

I have a custom ContracteResolver and I am getting unpredictable results.

Using the debugger, I see that when I serialize the CreateProperty method of the resolver is called for every property. However, if I make 2 calls back to back the CreateProperty method is not called on the second call. My breakpoint in CreateProperty is never hit on the second pass, it is hit on the first.

Here is my setup:

IContractResolver contractResolver = new ShouldSerializeContractResolver(fieldsToSerialize, this.Data);
var settings = new JsonSerializerSettings()
{
ContractResolver = contractResolver
};

_payload = JsonConvert.SerializeObject(this.Data, Formatting.None, settings);

My source value (this.Data) is different for both calls. The results (_payload) is also different for both call. I don't think anything is being cached.

I saw a similar question that was caused by a custom ContentNegotiator, but I don't use that.

Why would CreateProperty not be hit on the second pass?

Don Chambers
  • 3,798
  • 9
  • 33
  • 74
  • What does `ShouldSerializeContractResolver` inherit from? Is it `DefaultContractResolver` or `CamelCasePropertyNamesContractResolver`? – dbc Sep 21 '18 at 18:20
  • Because if you're inheriting from `CamelCasePropertyNamesContractResolver` it caches all contract information **globally** for the duration of the app domain. See [Json.Net: Html Helper Method not regenerating](https://stackoverflow.com/q/30740734/3744182) for details. – dbc Sep 21 '18 at 18:29

1 Answers1

5

The DefaultContractResolver class caches the contracts for each object type for best performance. You can see this in the source code where it creates the cache inside the constructor:

public DefaultContractResolver()
{
   ...
    _contractCache = new ThreadSafeStore<Type, JsonContract>(CreateContract);
}

And also how it is used when the contracts are resolved for each type:

public virtual JsonContract ResolveContract(Type type)
{
    ValidationUtils.ArgumentNotNull(type, nameof(type));

    return _contractCache.Get(type);
}

So if:

  1. your custom resolver derives from DefaultContractResolver, and
  2. you are using the same resolver instance each time, and
  3. your data objects are of the same type (even if they have different data)

then it is normal and expected that CreateProperty will only be called for the first serialization.

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • 4
    Almost the answer. I am not using the same resolver instance but I am inheriting from CamelCasePropertyNamesContractResolver which caches in a static variable. If I inherit from DefaultContractResolver it works, but I lose my CamelCase. How do I use two resolvers? I am adding and removing fields based on the data. – Don Chambers Sep 21 '18 at 20:00
  • 1
    There is not a way to use two resolvers at once. If you just need the camel casing behavior and you're using Json.Net 9.0 or later, you can set the [NamingStrategy](https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Serialization_DefaultContractResolver_NamingStrategy.htm) property on the default resolver (or your custom derivative) to [CamelCaseNamingStrategy](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Serialization_CamelCaseNamingStrategy.htm) get the camel case naming. – Brian Rogers Sep 21 '18 at 20:11
  • I have the same issue, but it does not catch it at all. – Fatemeh Ramezani Jan 13 '22 at 07:36