Does Json.NET cache types' serialization information?

c# expression-trees json json.net serialization

Question

In .NET world, when it comes to object serialization, it usually goes into inspecting the object's fields and properties at runtime. Using reflection for this job is usually slow and is undesirable when dealing with large sets of objects. The other way is using IL emit or building expression trees that provide significant performance gain over reflection. And the latter is most modern libraries pick when dealing with serialization. However building and emitting IL at runtime takes time, and the investment is only paid back if this information is cached and reused for objects of the same type.

When using Json.NET, it is not clear to me which method described above is used, and if the latter is indeed used, whether the caching is used.

For example, when I do:

JsonConvert.SerializeObject(new Foo { value = 1 });

Does Json.NET build the Foo's member access info and cache to reuse it later?

Accepted Answer

Json.NET caches type serialization information inside its IContractResolver classes DefaultContractResolver and CamelCasePropertyNamesContractResolver. Unless you specify a custom contract resolver, this information is cached and reused.

For DefaultContractResolver a global static instance is maintained internally that Json.NET uses whenever the application does not specify its own contract resolver. CamelCasePropertyNamesContractResolver, on the other hand, maintains static tables that are shared across all instances. (I believe the inconsistency arises from legacy issues; see here for details.)

Both of these types are designed to be fully thread-safe so sharing between threads should not be a problem.

If you choose to make your own contract resolver, then type information will only be cached and reused if you cache and reuse the contract resolver instance itself. Thus, for "stateless" resolvers (ones that do not modify their return values based on the current instance being serialized, or other runtime conditions), Newtonsoft recommends:

Cache instances of the contract resolver within your application for optimal performance.

One strategy for guaranteeing caching in a subclass of DefaultContractResolver is to make its constructor be protected or private, and provide a global static instance. (Of course this is only appropriate if the resolver should always return the same results.) E.g., inspired by this question, here's a pascal case to underscore contract resolver:

public class PascalCaseToUnderscoreContractResolver : DefaultContractResolver
{
    protected PascalCaseToUnderscoreContractResolver() : base() { }

    // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
    // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
    // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
    // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
    static PascalCaseToUnderscoreContractResolver instance;

    // Using an explicit static constructor enables lazy initialization.
    static PascalCaseToUnderscoreContractResolver() { instance = new PascalCaseToUnderscoreContractResolver(); }

    public static PascalCaseToUnderscoreContractResolver Instance { get { return instance; } }

    static string PascalCaseToUnderscore(string name)
    {
        if (name == null || name.Length < 1)
            return name;
        var sb = new StringBuilder(name);
        for (int i = 0; i < sb.Length; i++)
        {
            var ch = char.ToLowerInvariant(sb[i]);
            if (ch != sb[i])
            {
                if (i > 0) // Handle flag delimiters
                {
                    sb.Insert(i, '_');
                    i++;
                }
                sb[i] = ch;
            }
        }
        return sb.ToString();
    }

    protected override string ResolvePropertyName(string propertyName)
    {
        return PascalCaseToUnderscore(propertyName);
    }
}

Which you would use like:

var json = JsonConvert.SerializeObject(someObject, new JsonSerializerSettings { ContractResolver = PascalCaseToUnderscoreContractResolver.Instance });


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why