Json.NET 是不是缓存类型的序列化信息?

Posted

技术标签:

【中文标题】Json.NET 是不是缓存类型的序列化信息?【英文标题】:Does Json.NET cache types' serialization information?Json.NET 是否缓存类型的序列化信息? 【发布时间】:2016-02-07 01:34:27 【问题描述】:

在 .NET 世界中,当涉及到对象序列化时,通常会在运行时检查对象的字段和属性。对这项工作使用反射通常很慢,并且在处理大量对象时是不可取的。另一种方法是使用 IL 发射或构建表达式树,它们提供了比反射显着的性能增益。后者是大多数现代图书馆在处理序列化时选择的。然而,在运行时构建和发布 IL 需要时间,而且只有在缓存这些信息并为相同类型的对象重用时才能收回投资。

在使用Json.NET的时候,我并不清楚使用了上面描述的哪种方法,如果确实使用了后者,是否使用了缓存。

例如,当我这样做时:

JsonConvert.SerializeObject(new Foo  value = 1 );

Json.NET 是否构建 Foo 的成员访问信息并缓存以供以后重用?

【问题讨论】:

我没有给你一个明确的答案,但Json.NET 的来源在 github 上,它确实说“Json.NET 是一个流行的高性能 JSON 框架,用于 .NET” .如果您在源上快速搜索缓存,您会发现确实有相当多的缓存正在进行。 【参考方案1】:

是的。 Json.NET 在其IContractResolverDefaultContractResolverCamelCasePropertyNamesContractResolver 中缓存类型序列化信息。除非您指定自定义合同解析器,否则此信息将被缓存并重复使用。

对于DefaultContractResolver,在内部维护一个全局静态实例,只要应用程序未指定其自己的合约解析器,Json.NET 就会使用该实例。另一方面,CamelCasePropertyNamesContractResolver 维护在所有实例之间共享的静态表。 (我认为不一致是由遗留问题引起的;有关详细信息,请参阅here。)

这两种类型都被设计为完全线程安全的,因此线程之间的共享应该不是问题。

如果您选择实现和实例化您自己的合约解析器,那么只有在您缓存和重用合约解析器实例本身时,类型信息才会被缓存和重用。因此,Newtonsoft recommends:

为了提高性能,您应该创建一次合同解析器并尽可能重用实例。解析合约很慢,IContractResolver 的实现通常会缓存合约。

如果内存消耗是一个问题,无论出于何种原因,您需要最小化缓存合约永久占用的内存,您可以构建自己的 DefaultContractResolver 本地实例(或一些自定义子类),使用它进行序列化,然后立即删除对它的所有引用,例如:

public class JsonExtensions

    public static string SerializeObjectNoCache<T>(T obj, JsonSerializerSettings settings = null)
    
        settings = settings ?? new JsonSerializerSettings();
        bool reset = (settings.ContractResolver == null);
        if (reset)
            // To reduce memory footprint, do not cache contract information in the global contract resolver.
            settings.ContractResolver = new DefaultContractResolver();
        try
        
            return JsonConvert.SerializeObject(obj, settings);
        
        finally
        
            if (reset)
                settings.ContractResolver = null;
        
    

如果您使用的是CamelCasePropertyNamesContractResolver,请使用适当的naming strategy 切换到DefaultContractResolver,例如:

settings.ContractResolver = new DefaultContractResolver  NamingStrategy = new CamelCaseNamingStrategy() ;

大部分缓存的合约内存 (but not all) 最终会被垃圾回收。当然,这样做,序列化性能可能会受到很大影响。 (一些包含诸如enum 类型和数据协定属性的反映信息的表是全局共享的,不会被回收。)

有关详细信息,请参阅 Newtonsoft 的 Performance Tips: Reuse Contract Resolver。

【讨论】:

我们在 UT 中看到保存在 MyContractResolver : CamelCasePropertyNamesContractResolver 类型中的合约是静态缓存的,即使跨多个 MyContractResolver 类型也是如此。当一些 UT 一起运行时,这会导致令人惊讶的失败,因为它们没有根据需要设置合约。重用同一 ContractResolver 实例的指针是否仍然有效? @ElFik - CamelCasePropertyNamesContractResolver 的行为与 DefaultContractResolver 不同——无论您是否愿意,它都会全局缓存合约信息。如果您不想这样,请使用适当的命名策略切换到 DefaultContractResolver @dbc 和 ElFik 你拯救了我的一天!这是 CamelCasePropertyNamesContractResolver 实现的可怕失败。非常感谢!

以上是关于Json.NET 是不是缓存类型的序列化信息?的主要内容,如果未能解决你的问题,请参考以下文章

一:Newtonsoft.Json 支持序列化与反序列化的.net 对象类型;

使用 JSON.NET 反序列化任何内容

序列化特定类型时如何使 JSON.Net 序列化程序调用 ToString()?

使用JSON.Net(Newtonsoft.Json)作为ASP.Net MVC的json序列化和反序列化工具

使用 Json.Net 序列化哈希表

无法将 JSON 数组反序列化为类型 - Json.NET