Содержит ли Json.NET данные о сериализации типов кэша?

В мире.NET, когда дело доходит до сериализации объекта, он обычно проверяет поля и свойства объекта во время выполнения. Использование отражения для этой работы обычно медленное и нежелательно при работе с большими наборами объектов. Другой способ - использование IL-генерации или построение деревьев выражений, которые обеспечивают значительный прирост производительности по сравнению с отражением. И последний наиболее современные библиотеки выбирают при работе с сериализацией. Однако создание и испускание IL во время выполнения требуют времени, и инвестиции окупаются только в том случае, если эта информация кэшируется и повторно используется для объектов того же типа.

При использовании Json.NET мне не ясно, какой метод описан выше, и если последний действительно используется, используется ли кэширование.

Например, когда я делаю:

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

Создает ли Json.NET информацию о доступе к члену Foo и кэш, чтобы использовать ее позже?

1 ответ

Решение

Json.NET кэширует информацию сериализации типов внутри IContractResolver классы DefaultContractResolver а также CamelCasePropertyNamesContractResolver, Если вы не укажете пользовательский обработчик контракта, эта информация кэшируется и используется повторно.

За DefaultContractResolver глобальный статический экземпляр поддерживается внутри, который Json.NET использует всякий раз, когда приложение не указывает свой собственный обработчик контрактов. CamelCasePropertyNamesContractResolver с другой стороны, поддерживает статические таблицы, которые являются общими для всех экземпляров. (Я считаю, что несоответствие возникает из-за устаревших проблем; подробности см. Здесь.)

Оба эти типа предназначены для обеспечения полной безопасности потоков, поэтому совместное использование потоков не должно быть проблемой.

Если вы решите создать собственный обработчик контрактов, то информация о типах будет кэшироваться и использоваться повторно только в том случае, если вы кэшируете и повторно используете сам экземпляр распознавателя контрактов. Таким образом, для распознавателей без сохранения состояния (тех, которые не изменяют свои возвращаемые значения на основе сериализованного текущего экземпляра или других условий выполнения), Newtonsoft рекомендует:

Кэшируйте экземпляры решателя контрактов в вашем приложении для оптимальной производительности.

Одна стратегия для гарантии кэширования в подклассе DefaultContractResolver это сделать его конструктор защищенным или закрытым и предоставить глобальный статический экземпляр. (Конечно, это уместно, только если резолвер всегда должен возвращать одни и те же результаты.) Например, вдохновленный этим вопросом, вот случай с Паскалем, чтобы подчеркнуть решатель контракта:

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);
    }
}

Который вы бы использовали как:

var json = JsonConvert.SerializeObject(someObject, new JsonSerializerSettings { ContractResolver = PascalCaseToUnderscoreContractResolver.Instance });
Другие вопросы по тегам