Сравните Newtonsoft.Json с System.Text.Jsonи выполните миграцию в System.Text.Json

В этой статье описывается миграция из Newtonsoft.Json в System.Text.Json.

Пространство имен System.Text.Json предоставляет функциональные возможности для сериализации в нотацию объектов JavaScript (JSON) и десериализации объектов из этой нотации. Библиотека System.Text.Json включена в среду выполнения для .NET Core 3.1 и более поздних версий. Для других целевых платформ установите пакет NuGet System.Text.Json. Пакет поддерживает:

  • .NET Standard 2.0 и более поздних версий
  • .NET Framework 4.7.2 и более поздних версий
  • .NET Core 2.0, 2.1 и 2.2

System.Text.Json основное внимание уделяет требованиям, предъявляемым к производительности, безопасности и стандартам. У него есть некоторые ключевые отличия в поведении по умолчанию, и он не стремится к равенству функций с Newtonsoft.Json. В некоторых сценариях System.Text.Json в настоящее время нет встроенных функций, но существуют рекомендуемые обходные пути. В других сценариях обходные пути нецелесообразны.

Мы инвестируем в добавление функций, которые чаще всего запрашивались. Если приложение зависит от отсутствующего компонента, рассмотрите возможность регистрации проблемы в репозитории GitHub dotnet/runtime, чтобы узнать, можно ли добавить поддержку вашего сценария. Ознакомьтесь с эпической проблемой No 43620 , чтобы узнать, что уже запланировано.

Большая часть этой статьи посвящена использованию API JsonSerializer, но также содержит рекомендации по использованию JsonDocument (который представляет модель DOM), Utf8JsonReader и типов Utf8JsonWriter.

В Visual Basic нельзя использовать Utf8JsonReader, что также означает, что вы не можете писать пользовательские преобразователи. Большинство представленных здесь обходных решений требуют написания пользовательских преобразователей. Вы можете написать настраиваемый преобразователь на C# и зарегистрировать его в проекте Visual Basic. Дополнительные сведения см. в разделе Поддержка Visual Basic.

Таблица различий между Newtonsoft.Json и System.Text.Json

В следующей таблице перечислены функции Newtonsoft.Json и эквиваленты System.Text.Json. Эквиваленты делятся на следующие категории:

  • ✔️ Поддерживается встроенными функциями. Для получения подобного поведения в System.Text.Json может потребоваться использование атрибута или глобального параметра.
  • ⚠️ Не поддерживается, но возможно обходное решение. В качестве обходных путей можно использовать пользовательские преобразователи, которые могут не обеспечивать полное равенство с функциями Newtonsoft.Json. Для некоторых случаев приведены примеры кода. Если вы используете эти функции Newtonsoft.Json, для миграции потребуется внести изменения в объектные модели .NET или другие части кода.
  • ❌ Не поддерживается, а обходной путь не является практическим или возможным. Если вы используете эти функции Newtonsoft.Json, миграция будет невозможна без существенных изменений.
Функция Newtonsoft.Json Эквивалент System.Text.Json
Десериализация без учета регистра по умолчанию ✔️ Глобальный параметр PropertyNameCaseInsensitive
Имена свойств в "верблюжьем" стиле ✔️ Глобальный параметр PropertyNamingPolicy
Минимальное экранирование символов ✔️ Строгое экранирование символов, возможность настройки
Глобальный параметр NullValueHandling.Ignore ✔️ Глобальный параметр DefaultIgnoreCondition
Возможность комментариев ✔️ Глобальный параметр ReadCommentHandling
Возможность конечных запятых ✔️ Глобальный параметр AllowTrailingCommas
Регистрация пользовательского преобразователя ✔️ Очередность применения различается
По умолчанию отсутствует максимальная глубина ✔️ Максимальная глубина по умолчанию —64, настраиваемая
Глобальный параметр PreserveReferencesHandling ✔️ Глобальный параметр ReferenceHandling
Сериализация или десериализация чисел в кавычках ✔️ Глобальный параметр NumberHandling, атрибут [JsonNumberHandling]
Десериализация в неизменяемые классы и структуры ✔️ JsonConstructor, записи C# 9
Поддержка полей ✔️ Глобальный параметр IncludeFields, атрибут [JsonInclude]
Глобальный параметр DefaultValueHandling ✔️ Глобальный параметр DefaultIgnoreCondition
УстановкаNullValueHandling для атрибута [JsonProperty] ✔️ Атрибут JsonIgnore
УстановкаDefaultValueHandling для атрибута [JsonProperty] ✔️ Атрибут JsonIgnore
Десериализация Dictionary с ключом, не являющимся строкой ✔️ Поддерживается
Поддержка методов задания и методов получения свойств, которые не являются общими ✔️ Атрибут JsonInclude
Атрибут [JsonConstructor] ✔️ Атрибут [JsonConstructor]
Глобальный параметр ReferenceLoopHandling ✔️ Глобальный параметр ReferenceHandling
Обратные вызовы ✔️ Обратные вызовы
NaN, Бесконечность, -Бесконечность ✔️ Поддерживается
Установка Required для атрибута [JsonProperty] ✔️ Атрибут [JsonRequired] и обязательный модификатор C#
DefaultContractResolver для игнорирования свойств ✔️ Класс DefaultJsonTypeInfoResolver
Полиморфная сериализация ✔️ Атрибут [JsonDerivedType]
Полиморфная десериализация ✔️ Дискриминатор типа для атрибута [JsonDerivedType]
Поддержка широкого спектра типов ⚠️ Для некоторых типов требуются пользовательские преобразователи
Десериализация выводимого типа в свойства object ⚠️ Не поддерживается, обходное решение, пример
Десериализация литерала JSON null в типы значений, не допускающие значения NULL ⚠️ Не поддерживается, обходное решение, пример
Параметры DateTimeZoneHandling, DateFormatString ⚠️ Не поддерживается, обходное решение, пример
Метод JsonConvert.PopulateObject ⚠️ Не поддерживается, обходной путь
Глобальный параметр ObjectCreationHandling ⚠️ Не поддерживается, обходной путь
Добавление в коллекции без методов задания ⚠️ Не поддерживается, обходной путь
Имена свойств в змеином регистре ⚠️ Не поддерживается, обходной путь
Поддержка атрибутов System.Runtime.Serialization Не поддерживается
Глобальный параметр MissingMemberHandling Не поддерживается
Возможность имен свойств без кавычек Не поддерживается
Возможность одиночных кавычек вокруг строковых значений Не поддерживается
Возможность нестроковых значений JSON для строковых свойств Не поддерживается
Глобальный параметр TypeNameHandling.All Не поддерживается
JsonPath Поддержка запросов Не поддерживается
Настраиваемые ограничения Не поддерживается
Функция Newtonsoft.Json Эквивалент System.Text.Json
Десериализация без учета регистра по умолчанию ✔️ Глобальный параметр PropertyNameCaseInsensitive
Имена свойств в "верблюжьем" стиле ✔️ Глобальный параметр PropertyNamingPolicy
Минимальное экранирование символов ✔️ Строгое экранирование символов, возможность настройки
Глобальный параметр NullValueHandling.Ignore ✔️ Глобальный параметр DefaultIgnoreCondition
Возможность комментариев ✔️ Глобальный параметр ReadCommentHandling
Возможность конечных запятых ✔️ Глобальный параметр AllowTrailingCommas
Регистрация пользовательского преобразователя ✔️ Очередность применения различается
По умолчанию отсутствует максимальная глубина ✔️ Максимальная глубина по умолчанию —64, настраиваемая
Глобальный параметр PreserveReferencesHandling ✔️ Глобальный параметр ReferenceHandling
Сериализация или десериализация чисел в кавычках ✔️ Глобальный параметр NumberHandling, атрибут [JsonNumberHandling]
Десериализация в неизменяемые классы и структуры ✔️ JsonConstructor, записи C# 9
Поддержка полей ✔️ Глобальный параметр IncludeFields, атрибут [JsonInclude]
Глобальный параметр DefaultValueHandling ✔️ Глобальный параметр DefaultIgnoreCondition
УстановкаNullValueHandling для атрибута [JsonProperty] ✔️ Атрибут JsonIgnore
УстановкаDefaultValueHandling для атрибута [JsonProperty] ✔️ Атрибут JsonIgnore
Десериализация Dictionary с ключом, не являющимся строкой ✔️ Поддерживается
Поддержка методов задания и методов получения свойств, которые не являются общими ✔️ Атрибут JsonInclude
Атрибут [JsonConstructor] ✔️ Атрибут [JsonConstructor]
Глобальный параметр ReferenceLoopHandling ✔️ Глобальный параметр ReferenceHandling
Обратные вызовы ✔️ Обратные вызовы
NaN, Бесконечность, -Бесконечность ✔️ Поддерживается
Поддержка широкого спектра типов ⚠️ Для некоторых типов требуются пользовательские преобразователи
Полиморфная сериализация ⚠️ Не поддерживается, обходное решение, пример
Полиморфная десериализация ⚠️ Не поддерживается, обходное решение, пример
Десериализация выводимого типа в свойства object ⚠️ Не поддерживается, обходное решение, пример
Десериализация литерала JSON null в типы значений, не допускающие значения NULL ⚠️ Не поддерживается, обходное решение, пример
Установка Required для атрибута [JsonProperty] ⚠️ Не поддерживается, обходное решение, пример
DefaultContractResolver для игнорирования свойств ⚠️ Не поддерживается, обходное решение, пример
Параметры DateTimeZoneHandling, DateFormatString ⚠️ Не поддерживается, обходное решение, пример
Метод JsonConvert.PopulateObject ⚠️ Не поддерживается, обходной путь
Глобальный параметр ObjectCreationHandling ⚠️ Не поддерживается, обходной путь
Добавление в коллекции без методов задания ⚠️ Не поддерживается, обходной путь
Имена свойств в змеином регистре ⚠️ Не поддерживается, обходной путь
Поддержка атрибутов System.Runtime.Serialization Не поддерживается
Глобальный параметр MissingMemberHandling Не поддерживается
Возможность имен свойств без кавычек Не поддерживается
Возможность одиночных кавычек вокруг строковых значений Не поддерживается
Возможность нестроковых значений JSON для строковых свойств Не поддерживается
Глобальный параметр TypeNameHandling.All Не поддерживается
JsonPath Поддержка запросов Не поддерживается
Настраиваемые ограничения Не поддерживается
Функция Newtonsoft.Json Эквивалент System.Text.Json
Десериализация без учета регистра по умолчанию ✔️ Глобальный параметр PropertyNameCaseInsensitive
Имена свойств в "верблюжьем" стиле ✔️ Глобальный параметр PropertyNamingPolicy
Минимальное экранирование символов ✔️ Строгое экранирование символов, возможность настройки
Глобальный параметр NullValueHandling.Ignore ✔️ Глобальный параметр IgnoreNullValues
Возможность комментариев ✔️ Глобальный параметр ReadCommentHandling
Возможность конечных запятых ✔️ Глобальный параметр AllowTrailingCommas
Регистрация пользовательского преобразователя ✔️ Очередность применения различается
По умолчанию отсутствует максимальная глубина ✔️ Максимальная глубина по умолчанию —64, настраиваемая
Поддержка широкого спектра типов ⚠️ Для некоторых типов требуются пользовательские преобразователи
Десериализация строк как чисел ⚠️ Не поддерживается, обходной путь, пример
Десериализация Dictionary с ключом, не являющимся строкой ⚠️ Не поддерживается, обходной путь, пример
Полиморфная сериализация ⚠️ Не поддерживается, обходной путь, пример
Полиморфная десериализация ⚠️ Не поддерживается, обходной путь, пример
Десериализация выводимого типа в свойства object ⚠️ Не поддерживается, обходной путь, пример
Десериализация литерала JSON null в типы значений, не допускающие значения NULL ⚠️ Не поддерживается, обходной путь, пример
Десериализация в неизменяемые классы и структуры ⚠️ Не поддерживается, обходной путь, пример
Атрибут [JsonConstructor] ⚠️ Не поддерживается, обходной путь, пример
Установка Required для атрибута [JsonProperty] ⚠️ Не поддерживается, обходной путь, пример
Установка NullValueHandling для атрибута [JsonProperty] ⚠️ Не поддерживается, обходной путь, пример
Установка DefaultValueHandling для атрибута [JsonProperty] ⚠️ Не поддерживается, обходной путь, пример
Глобальный параметр DefaultValueHandling ⚠️ Не поддерживается, обходной путь, пример
DefaultContractResolver для игнорирования свойств ⚠️ Не поддерживается, обходной путь, пример
Параметры DateTimeZoneHandling, DateFormatString ⚠️ Не поддерживается, обходной путь, пример
Обратные вызовы ⚠️ Не поддерживается, обходной путь, пример
Поддержка открытых и не открытых полей ⚠️ Не поддерживается, обходной путь
Поддержка методов задания и методов получения свойств, которые не являются общими ⚠️ Не поддерживается, обходной путь
Метод JsonConvert.PopulateObject ⚠️ Не поддерживается, обходной путь
Глобальный параметр ObjectCreationHandling ⚠️ Не поддерживается, обходной путь
Добавление в коллекции без методов задания ⚠️ Не поддерживается, обходной путь
Имена свойств в змеином регистре ⚠️ Не поддерживается, обходной путь
NaN, Бесконечность, -Infinity ⚠️ Не поддерживается, обходной путь
Глобальный параметр PreserveReferencesHandling Не поддерживается
Глобальный параметр ReferenceLoopHandling Не поддерживается
Поддержка атрибутов System.Runtime.Serialization Не поддерживается
Глобальный параметр MissingMemberHandling Не поддерживается
Возможность имен свойств без кавычек Не поддерживается
Возможность одиночных кавычек вокруг строковых значений Не поддерживается
Возможность нестроковых значений JSON для строковых свойств Не поддерживается
Глобальный параметр TypeNameHandling.All Не поддерживается
JsonPath Поддержка запросов Не поддерживается
Настраиваемые ограничения Не поддерживается

Это неполный список функций Newtonsoft.Json. В список входят многие сценарии, которые были запрошены в проблемах GitHub или записях StackOverflow. Если вы реализуете обходной путь для одного из перечисленных здесь сценариев, для которого в настоящее время нет примера кода, и если вы хотите поделиться своим решением, нажмите Эта страница в разделе Отзывы (в нижней части этой страницы). Это позволит создать проблему в репозитории GitHub в этой документации и указать ее в разделе Отзывы на этой странице.

Различия в поведении JsonSerializer по умолчанию по сравнению с Newtonsoft.Json

System.Text.Json по умолчанию является строгим и избегает двусмысленностей со стороны вызывающего объекта, подчеркивая детерминированное поведение. Библиотека преднамеренно разработана таким образом для повышения производительности и безопасности. Newtonsoft.Json по умолчанию является гибким. Это фундаментальное различие в проектировании обуславливает многие из следующих различий в поведении по умолчанию.

Десериализация без учета регистра

Во время десериализации Newtonsoft.Json выполняет сопоставление имени свойства без учета регистра по умолчанию. System.Text.Json по умолчанию учитывает регистр, что обеспечивает более высокую производительность, так как соответствие является точным. Сведения о том, как выполнять сопоставление без учета регистра, см. в разделе Сопоставление свойств без учета регистра.

Если вы используете System.Text.Json косвенно с помощью ASP.NET Core, вам не нужно ничего делать для получения поведения, аналогичного Newtonsoft.Json. ASP.NET Core задает параметры для имен свойств в "верблюжьем" стиле и сопоставления без учета регистра при использовании System.Text.Json.

ASP.NET Core также по умолчанию позволяет десериализовать заключенные в кавычки числа.

Минимальное экранирование символов

Во время сериализации Newtonsoft.Json обеспечивает относительную свободу, разрешая символы без экранирования. То есть он не заменяет их на \uxxxx, где xxxx является кодовой точкой символа. Когда он использует экранирование, он выдает \ перед символом (например, " преобразуется в \"). System.Text.Json по умолчанию экранирует больше символов, чтобы обеспечить глубокую защиту от межсайтового скриптинга (XSS) или атак с раскрытием информации и делает это с помощью последовательности из шести символов. System.Text.Json экранирует все символы, отличные от ASCII, по умолчанию, поэтому вам не нужно ничего делать, если вы используете StringEscapeHandling.EscapeNonAscii в Newtonsoft.Json. System.Text.Json также по умолчанию экранирует символы, учитывающие HTML. Сведения о том, как переопределить поведение System.Text.Json по умолчанию, см. в разделе Настройка кодировки символов.

Комментарии

Во время десериализации Newtonsoft.Json по умолчанию игнорирует комментарии в JSON. System.Text.Json по умолчанию выдает исключения для комментариев, так как спецификация RFC 8259 не включает их. Сведения о том, как разрешить комментарии, см. в разделе Возможность комментариев и конечных запятых.

Конечные запятые

Во время десериализации Newtonsoft.Json по умолчанию игнорирует конечные запятые. Он также игнорирует несколько конечных запятых (например, [{"Color":"Red"},{"Color":"Green"},,]). System.Text.Json по умолчанию выдает исключения для конечных запятых, так как спецификация RFC 8259 не разрешает их. Сведения о том, как заставить System.Text.Json их принять, см. в разделе Возможность комментариев и конечных запятых. Невозможно разрешить несколько конечных запятых.

Очередность регистрации преобразователей

Очередность регистрации Newtonsoft.Json для пользовательских преобразователей выглядит следующим образом.

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

Очередность регистрации System.Text.Json для пользовательских преобразователей выглядит иначе:

  • Атрибут для свойства
  • Коллекция Converters
  • Атрибут для типа

Разница заключается в том, что пользовательский преобразователь в коллекции Converters переопределяет атрибут на уровне типа. Цель этой очередности заключается в том, чтобы изменения во время выполнения переопределяли варианты во время разработки. Изменить очередность невозможно.

Дополнительные сведения о регистрации пользовательских преобразователей см. в разделе Регистрация пользовательского преобразователя.

Максимальная глубина

Максимальная глубина последней Newtonsoft.Json версии по умолчанию составляет 64. System.Text.Jsonтакже имеет ограничение по умолчанию 64, и его можно настроить, задав .JsonSerializerOptions.MaxDepth

Если вы используете System.Text.Json опосредованно через ASP.NET Core, максимальный предел глубины по умолчанию составляет 32. Значение по умолчанию будет таким же, как для привязки модели, и задаваться в классе JsonOptions.

Строки JSON (имена свойств и строковые значения)

Во время десериализации Newtonsoft.Json принимает имена свойств, заключенные в двойные кавычки, одинарные кавычки или без кавычек. Он принимает строковые значения, заключенные в двойные кавычки или одинарные кавычки. Например, Newtonsoft.Json принимает следующий код JSON:

{
  "name1": "value",
  'name2': "value",
  name3: 'value'
}

System.Text.Json принимает имена свойств и строковые значения только в двойных кавычках, так как этот формат требуется спецификацией RFC 8259 и является единственным форматом, который считается допустимым JSON.

Значение, заключенное в одинарные кавычки, приводит к исключению JsonException со следующим сообщением:

''' is an invalid start of a value.

Нестроковые значения для строковых свойств

Newtonsoft.Json принимает нестроковые значения, например числа или литералы true и false, для десериализации в свойства строкового типа. Ниже приведен пример JSON, который Newtonsoft.Json успешно десериализует в следующий класс:

{
  "String1": 1,
  "String2": true,
  "String3": false
}
public class ExampleClass
{
    public string String1 { get; set; }
    public string String2 { get; set; }
    public string String3 { get; set; }
}

System.Text.Json не выполняет десериализацию нестроковых значений в строковые свойства. Нестроковое значение, полученное для строкового поля, приводит к исключению JsonException со следующим сообщением:

The JSON value could not be converted to System.String.

Сценарии с использованием JsonSerializer

Некоторые из приведенных ниже сценариев не поддерживаются встроенными функциями, но возможны обходные пути. В качестве обходных путей можно использовать пользовательские преобразователи, которые могут не обеспечивать полное равенство с функциями Newtonsoft.Json. Для некоторых случаев приведены примеры кода. Если вы используете эти функции Newtonsoft.Json, для миграции потребуется внести изменения в объектные модели .NET или другие части кода.

Для некоторых из приведенных ниже сценариев обходные пути являются нецелесообразными или невозможными. Если вы используете эти функции Newtonsoft.Json, миграция будет невозможна без существенных изменений.

Разрешение или запись чисел в кавычках

Newtonsoft.Json может сериализовать или десериализовать числа, представленные строками JSON (заключенные в кавычки). Например, он может принимать {"DegreesCelsius":"23"} вместо {"DegreesCelsius":23}. Чтобы включить это поведение в System.Text.Json, задайте для свойства JsonSerializerOptions.NumberHandling значение WriteAsString или AllowReadingFromString или используйте атрибут [JsonNumberHandling].

Если вы используете System.Text.Json косвенно с помощью ASP.NET Core, вам не нужно ничего делать для получения поведения, аналогичного Newtonsoft.Json. ASP.NET Core указывает стандартные значения для веб-приложений при использовании System.Text.Json. Стандартные значения для веб-служб допускают заключенные в кавычки числа.

Дополнительные сведения см. в разделе Разрешение или запись чисел в кавычках.

Newtonsoft.Json может сериализовать или десериализовать числа, представленные строками JSON (заключенные в кавычки). Например, он может принимать {"DegreesCelsius":"23"} вместо {"DegreesCelsius":23}. Чтобы включить это поведение в System.Text.Json в .NET Core 3.1, реализуйте пользовательский преобразователь, как показано в следующем примере. Преобразователь обрабатывает свойства, определенные как long:

  • Он сериализует их как строки JSON.
  • Он принимает числа JSON и числа в кавычках при десериализации.
using System.Buffers;
using System.Buffers.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class LongToStringConverter : JsonConverter<long>
    {
        public override long Read(
            ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.String)
            {
                ReadOnlySpan<byte> span =
                    reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;

                if (Utf8Parser.TryParse(span, out long number, out int bytesConsumed) &&
                    span.Length == bytesConsumed)
                {
                    return number;
                }

                if (long.TryParse(reader.GetString(), out number))
                {
                    return number;
                }
            }

            return reader.GetInt64();
        }

        public override void Write(
            Utf8JsonWriter writer, long longValue, JsonSerializerOptions options) =>
            writer.WriteStringValue(longValue.ToString());
    }
}

Зарегистрируйте этот пользовательский преобразователь, используя атрибут для отдельных свойств long или добавив преобразователь в коллекцию Converters.

Указание конструктора для использования при десериализации

Атрибут Newtonsoft.Json[JsonConstructor] позволяет указать, какой конструктор вызывать при десериализации в POCO.

System.Text.Json также имеет атрибут [JsonConstructor]. Дополнительные сведения см. в разделе Неизменяемые типы и записи.

System.Text.Json в .NET Core 3.1 поддерживает только конструкторы без параметров. В качестве обходного решения можно вызвать нужный конструктор в пользовательском преобразователе. См. пример десериализации в неизменяемые классы и структуры.

Условное игнорирование свойства

Newtonsoft.Json имеет несколько способов условно игнорировать свойство при сериализации или десериализации:

  • DefaultContractResolver позволяет выбирать свойства для включения или игнорирования на основе произвольных критериев.
  • Параметры NullValueHandling и DefaultValueHandling для JsonSerializerSettings позволяют указать, что все свойства со значением NULL или значениями по умолчанию должны игнорироваться.
  • Параметры NullValueHandling и DefaultValueHandling атрибута [JsonProperty] позволяют указать отдельные свойства, которые должны игнорироваться при установке значения NULL или значения по умолчанию.

System.Text.Json предоставляет следующие способы игнорирования свойств или полей при сериализации:

Кроме того, в .NET 7 и более поздних версиях можно настроить контракт JSON, чтобы игнорировать свойства на основе произвольных критериев. Дополнительные сведения см. в разделе Пользовательские контракты.

System.Text.Json в .NET Core 3.1 обеспечивает следующие способы игнорирования свойств при сериализации:

  • Атрибут [JsonIgnore] в свойстве приводит к исключению свойства из JSON во время сериализации.
  • Глобальный параметр IgnoreNullValues позволяет игнорировать все свойства со значением NULL. IgnoreNullValues Не рекомендуется использовать в .NET 5 и более поздних версиях, поэтому intelliSense не отображает его. Сведения о том, как игнорировать значения NULL, см. в статье Как игнорировать все свойства значений NULL в .NET 5 и более поздних версиях.
  • Глобальный параметр IgnoreReadOnlyProperties позволяет игнорировать все свойства "только для чтения".

Эти параметры не позволяют игнорировать выбранные свойства на основе произвольных критериев, оцениваемых во время выполнения.

Кроме того, в .NET Core 3.1 невозможно:

  • Игнорировать все свойства, имеющие значение по умолчанию для типа.
  • Игнорировать выбранные свойства, имеющие значение по умолчанию для типа.
  • Игнорировать выбранные свойства, если их значение равно NULL.
  • Игнорировать выбранные свойства на основе произвольных критериев, вычисляемых во время выполнения.

Для этой функции можно написать пользовательский преобразователь. Ниже приведен пример POCO и пользовательского преобразователя, демонстрирующий этот подход:

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class WeatherForecastRuntimeIgnoreConverter : JsonConverter<WeatherForecast>
    {
        public override WeatherForecast Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.StartObject)
            {
                throw new JsonException();
            }

            var wf = new WeatherForecast();

            while (reader.Read())
            {
                if (reader.TokenType == JsonTokenType.EndObject)
                {
                    return wf;
                }

                if (reader.TokenType == JsonTokenType.PropertyName)
                {
                    string propertyName = reader.GetString()!;
                    reader.Read();
                    switch (propertyName)
                    {
                        case "Date":
                            DateTimeOffset date = reader.GetDateTimeOffset();
                            wf.Date = date;
                            break;
                        case "TemperatureCelsius":
                            int temperatureCelsius = reader.GetInt32();
                            wf.TemperatureCelsius = temperatureCelsius;
                            break;
                        case "Summary":
                            string summary = reader.GetString()!;
                            wf.Summary = string.IsNullOrWhiteSpace(summary) ? "N/A" : summary;
                            break;
                    }
                }
            }

            throw new JsonException();
        }

        public override void Write(Utf8JsonWriter writer, WeatherForecast wf, JsonSerializerOptions options)
        {
            writer.WriteStartObject();

            writer.WriteString("Date", wf.Date);
            writer.WriteNumber("TemperatureCelsius", wf.TemperatureCelsius);
            if (!string.IsNullOrWhiteSpace(wf.Summary) && wf.Summary != "N/A")
            {
                writer.WriteString("Summary", wf.Summary);
            }

            writer.WriteEndObject();
        }
    }
}

Преобразователь вызывает исключение свойства Summary из сериализации, если его значение — NULL, пустая строка или "N/A".

Зарегистрируйте этот пользовательский преобразователь, используя атрибут для класса или добавив преобразователь в коллекцию Converters.

Этот подход требует дополнительной логики, если:

  • POCO включает сложные свойства.
  • Необходимо выполнять обработку таких атрибутов, как [JsonIgnore], или таких параметров, как пользовательские кодировщики.

Открытые и не открытые поля

Newtonsoft.Json может выполнять сериализацию и десериализацию полей, а также свойств.

Чтобы включить общие поля при сериализации или десериализации, используйте в System.Text.Json глобальный параметр JsonSerializerOptions.IncludeFields или атрибут [JsonInclude]. Пример см. в разделе Включение полей.

System.Text.Json в .NET Core 3.1 работает только с общими свойствами. Эта функция может быть предоставлена пользовательскими преобразователями.

Сохранение ссылок на объекты и обработка циклов

По умолчанию Newtonsoft.Json сериализуется по значению. Например, если объект включает два свойства, которые содержат ссылку на один и тот же объект Person, значения свойств этого объекта Person дублируются в JSON.

У Newtonsoft.Json параметр PreserveReferencesHandling имеет значение JsonSerializerSettings, что позволяет выполнять сериализацию по ссылке:

  • Метаданные идентификатора добавляются в JSON, созданный для первого объекта Person.
  • JSON, созданный для второго объекта Person, содержит ссылку на этот идентификатор вместо значений свойств.

Newtonsoft.Json также имеет параметр ReferenceLoopHandling, который позволяет игнорировать циклические ссылки, а не создавать исключение.

Для сохранения ссылок и обработки циклических ссылок System.Text.Json задайте для свойства JsonSerializerOptions.ReferenceHandler значение Preserve. Параметр ReferenceHandler.Preserve эквивалентен PreserveReferencesHandling = PreserveReferencesHandling.All в Newtonsoft.Json.

Поведение ReferenceHandler.IgnoreCycles параметра аналогично .Newtonsoft.JsonReferenceLoopHandling.Ignore Одно из различий заключается в System.Text.Json том, что реализация заменяет ссылочные циклы маркером null JSON, а не игнорирует ссылку на объект. Дополнительные сведения см. в статье Пропуск циклических ссылок.

Как и Newtonsoft.JsonReferenceResolver, System.Text.Json.Serialization.ReferenceResolver класс определяет поведение сохранения ссылок при сериализации и десериализации. Создайте производный класс, чтобы указать настраиваемое поведение. Пример см. в статье GuidReferenceResolver.

Некоторые связанные функции Newtonsoft.Json не поддерживаются:

System.Text.Json в .NET Core 3.1 поддерживает сериализацию только по значению и вызывает исключение для циклических ссылок.

Словарь с ключом, не являющимся строкой

И Newtonsoft.Json, и System.Text.Json поддерживают коллекции типа Dictionary<TKey, TValue>. Однако в System.Text.JsonTKey должен быть примитивным типом, а не пользовательским типом. Дополнительные сведения см. в разделе Поддерживаемые типы ключей.

Внимание!

Десериализация в , Dictionary<TKey, TValue> где TKey типизируется как что-то другое, может string привести к уязвимости безопасности в приложении-потребителе. Дополнительные сведения см. в разделе dotnet/runtime#4761.

Newtonsoft.Json поддерживает коллекции типа Dictionary<TKey, TValue>. Встроенная поддержка коллекций словарей в System.Text.Json в .NET Core 3.1 ограничена Dictionary<string, TValue>. То есть ключ должен быть строкой.

Для поддержки словаря с целым числом или другим типом в качестве ключа в .NET Core 3.1 создайте преобразователь, аналогичный примеру в разделе Как писать пользовательские преобразователи.

Типы без встроенной поддержки

System.Text.Json не предоставляет встроенную поддержку для следующих типов:

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

Полиморфная сериализация

Newtonsoft.Json автоматически выполняет полиморфную сериализацию. Начиная с .NET 7, System.Text.Json поддерживает полиморфную сериализацию с помощью атрибута JsonDerivedTypeAttribute . Дополнительные сведения см. в разделе Сериализация свойств производных классов.

Полиморфная десериализация

Newtonsoft.JsonTypeNameHandling имеет параметр , который добавляет метаданные имени типа в JSON при сериализации. Он использует метаданные во время десериализации для выполнения полиморфной десериализации. Начиная с .NET 7, System.Text.Json для выполнения полиморфной десериализации используется информация о дискриминаторе типов. Эти метаданные создаются в JSON, а затем используются во время десериализации, чтобы определить, следует ли десериализовать в базовый или производный тип. Дополнительные сведения см. в разделе Сериализация свойств производных классов.

Для поддержки полиморфной десериализации в более ранних версиях .NET создайте преобразователь, как в примере в разделе Создание пользовательских преобразователей.

Десериализация свойств объекта

Когда Newtonsoft.Json выполняет десериализацию в Object, он:

  • Выводит типы примитивных значений в полезных данных JSON (кроме null) и возвращает хранимые string, long, double, boolean или DateTime в виде упакованного объекта. Примитивные значения — это отдельные значения JSON, такие как число JSON, строка, true, false или null.
  • Возвращает JObject или JArray для сложных значений в полезных данных JSON. Сложные значения являются коллекциями пар "ключ-значение" JSON в фигурных скобках ({}) или списками значений в квадратных скобках ([]). Свойства и значения в фигурных или квадратных скобках могут иметь дополнительные свойства или значения.
  • Возвращает пустую ссылку, если полезная нагрузка содержит литерал null JSON.

System.Text.Json хранит упакованный JsonElement как для простых, так и для сложных значений при десериализации в Object, например:

  • Свойство object.
  • Значение словаря object.
  • Тип массива object.
  • Корневой object.

Однако System.Text.Json обрабатывает null так же, как Newtonsoft.Json, и возвращает пустую ссылку, если полезная нагрузка содержит литерал null JSON.

Чтобы реализовать определение типа для свойств object, создайте преобразователь, как в примере в разделе Написание пользовательских преобразователей.

Десериализация значения NULL в тип, не допускающий значения NULL

Newtonsoft.Json не создает исключение в следующем сценарии:

  • NullValueHandling имеет значение Ignore.
  • Во время десериализации JSON содержит значение NULL для типа значения, не допускающего значения NULL.

В том же сценарии System.Text.Json создает исключение. (Соответствующий параметр обработки значений NULL в System.Text.JsonJsonSerializerOptions.IgnoreNullValues = true.)

Если вы владеете типом целевого объекта, лучшим обходным решением будет сделать соответствующее свойство допускающим значение NULL (например, изменить int на int?).

Еще один обходной путь — сделать преобразователь для типа, как в следующем примере, который обрабатывает значения NULL для типов DateTimeOffset:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class DateTimeOffsetNullHandlingConverter : JsonConverter<DateTimeOffset>
    {
        public override DateTimeOffset Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options) =>
            reader.TokenType == JsonTokenType.Null
                ? default
                : reader.GetDateTimeOffset();

        public override void Write(
            Utf8JsonWriter writer,
            DateTimeOffset dateTimeValue,
            JsonSerializerOptions options) =>
            writer.WriteStringValue(dateTimeValue);
    }
}

Зарегистрируйте этот пользовательский преобразователь, используя атрибут для свойства или добавив преобразователь в коллекцию Converters.

Примечание. Предыдущий преобразователь обрабатывает значения NULL иначе, чем Newtonsoft.Json для POCO, которые указывают значения по умолчанию. Например, следующий код представляет целевой объект:

public class WeatherForecastWithDefault
{
    public WeatherForecastWithDefault()
    {
        Date = DateTimeOffset.Parse("2001-01-01");
        Summary = "No summary";
    }
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
}

Предположим, что следующий JSON десериализуется с помощью предыдущего преобразователя:

{
  "Date": null,
  "TemperatureCelsius": 25,
  "Summary": null
}

После десериализации свойство Date имеет значение 1/1/0001 (default(DateTimeOffset)), то есть значение, заданное в конструкторе, перезаписывается. При наличии одних и тех же POCO и JSON десериализация Newtonsoft.Json оставляет значение 1/1/2001 в свойстве Date.

Десериализация в неизменяемые классы и структуры

Newtonsoft.Json может выполнять десериализацию в неизменяемые классы и структуры, так как он может использовать конструкторы с параметрами.

Чтобы указать использование параметризованного конструктора, используйте вSystem.Text.Json атрибут [JsonConstructor]. Записи в C# 9 также являются неизменяемыми и поддерживаются в качестве целей десериализации. Дополнительные сведения см. в разделе Неизменяемые типы и записи.

System.Text.Json в .NET Core 3.1 поддерживает только общие конструкторы без параметров. В качестве обходного решения можно вызвать конструктор с параметрами в пользовательском преобразователе.

Ниже приведена неизменяемая структура с несколькими параметрами конструктора:

public readonly struct ImmutablePoint
{
    public ImmutablePoint(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; }
    public int Y { get; }
}

А вот преобразователь, который сериализует и десериализует эту структуру:

using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class ImmutablePointConverter : JsonConverter<ImmutablePoint>
    {
        private readonly JsonEncodedText _xName = JsonEncodedText.Encode("X");
        private readonly JsonEncodedText _yName = JsonEncodedText.Encode("Y");

        private readonly JsonConverter<int> _intConverter;

        public ImmutablePointConverter(JsonSerializerOptions options) => 
            _intConverter = options?.GetConverter(typeof(int)) is JsonConverter<int> intConverter
                ? intConverter
                : throw new InvalidOperationException();

        public override ImmutablePoint Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.StartObject)
            {
                throw new JsonException();
            };

            int? x = default;
            int? y = default;

            // Get the first property.
            reader.Read();
            if (reader.TokenType != JsonTokenType.PropertyName)
            {
                throw new JsonException();
            }

            if (reader.ValueTextEquals(_xName.EncodedUtf8Bytes))
            {
                x = ReadProperty(ref reader, options);
            }
            else if (reader.ValueTextEquals(_yName.EncodedUtf8Bytes))
            {
                y = ReadProperty(ref reader, options);
            }
            else
            {
                throw new JsonException();
            }

            // Get the second property.
            reader.Read();
            if (reader.TokenType != JsonTokenType.PropertyName)
            {
                throw new JsonException();
            }

            if (x.HasValue && reader.ValueTextEquals(_yName.EncodedUtf8Bytes))
            {
                y = ReadProperty(ref reader, options);
            }
            else if (y.HasValue && reader.ValueTextEquals(_xName.EncodedUtf8Bytes))
            {
                x = ReadProperty(ref reader, options);
            }
            else
            {
                throw new JsonException();
            }

            reader.Read();

            if (reader.TokenType != JsonTokenType.EndObject)
            {
                throw new JsonException();
            }

            return new ImmutablePoint(x.GetValueOrDefault(), y.GetValueOrDefault());
        }

        private int ReadProperty(ref Utf8JsonReader reader, JsonSerializerOptions options)
        {
            Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);

            reader.Read();
            return _intConverter.Read(ref reader, typeof(int), options);
        }

        private void WriteProperty(Utf8JsonWriter writer, JsonEncodedText name, int intValue, JsonSerializerOptions options)
        {
            writer.WritePropertyName(name);
            _intConverter.Write(writer, intValue, options);
        }

        public override void Write(
            Utf8JsonWriter writer,
            ImmutablePoint point,
            JsonSerializerOptions options)
        {
            writer.WriteStartObject();
            WriteProperty(writer, _xName, point.X, options);
            WriteProperty(writer, _yName, point.Y, options);
            writer.WriteEndObject();
        }
    }
}

Зарегистрируйте этот пользовательский преобразователь, добавив его в коллекцию Converters.

Пример подобного преобразователя, обрабатывающего открытые универсальные свойства, см. в разделе Встроенный преобразователь для пар "ключ-значение".

Обязательные свойства

В Newtonsoft.Json вы указываете, что свойство является обязательным, задав Required для атрибута [JsonProperty]. Newtonsoft.Json создает исключение, если в JSON не получено значение для свойства, помеченного как обязательное.

Начиная с .NET 7 для обязательного свойства можно использовать модификатор JsonRequiredAttribute C# required или атрибут . System.Text.Json Вызывает исключение, если полезные данные JSON не содержат значения для помеченного свойства. Дополнительные сведения см. в разделе Обязательные свойства.

System.Text.Json не создает исключение, если для одного из свойств целевого типа не получено значение. Например, у вас есть класс WeatherForecast:

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}

Следующий JSON десериализуется без ошибки:

{
    "TemperatureCelsius": 25,
    "Summary": "Hot"
}

Чтобы выполнить десериализацию, если в JSON нет Date свойства, выберите один из следующих параметров:

Следующий пример кода преобразователя создает исключение, если после десериализации свойство Date не задано:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class WeatherForecastRequiredPropertyConverter : JsonConverter<WeatherForecast>
    {
        public override WeatherForecast Read(
            ref Utf8JsonReader reader,
            Type type,
            JsonSerializerOptions options)
        {
            // Don't pass in options when recursively calling Deserialize.
            WeatherForecast forecast = JsonSerializer.Deserialize<WeatherForecast>(ref reader)!;

            // Check for required fields set by values in JSON
            return forecast!.Date == default
                ? throw new JsonException("Required property not received in the JSON")
                : forecast;
        }

        public override void Write(
            Utf8JsonWriter writer,
            WeatherForecast forecast, JsonSerializerOptions options)
        {
            // Don't pass in options when recursively calling Serialize.
            JsonSerializer.Serialize(writer, forecast);
        }
    }
}

Зарегистрируйте этот пользовательский преобразователь, добавив его в коллекцию JsonSerializerOptions.Converters.

Для использования этого шаблона рекурсивного вызова преобразователя необходимо зарегистрировать преобразователь с помощью JsonSerializerOptions, а не с помощью атрибута. При регистрации с помощью атрибута пользовательский преобразователь рекурсивно вызывает сам себя. Результатом является бесконечный цикл, который заканчивается исключением переполнения стека.

При регистрации преобразователя с помощью объекта options следует избегать бесконечного цикла, поэтому не передавайте объект options при рекурсивном вызове Serialize или Deserialize. Объект options содержит коллекцию Converters. Если передать его в Serialize или Deserialize, пользовательский преобразователь вызывает сам себя и делает бесконечный цикл, который приводит к исключению переполнения стека. Если параметры по умолчанию нецелесообразны, создайте новый экземпляр параметров с нужными настройками. Этот подход отнимет много времени, так как каждый новый экземпляр кэшируется независимо.

Существует альтернативный шаблон, который может использовать регистрацию JsonConverterAttribute в классе, подлежащем преобразованию. В таком случае код преобразователя вызывает Serialize или Deserialize для класса, производного от класса, который требуется преобразовать. К производному классу не применен JsonConverterAttribute. В следующем примере этого варианта:

  • WeatherForecastWithRequiredPropertyConverterAttribute — класс, который подлежит десериализации и к которому применен JsonConverterAttribute;
  • WeatherForecastWithoutRequiredPropertyConverterAttribute — производный класс без атрибута преобразователя.
  • Код в преобразователе вызывает Serialize и Deserialize , WeatherForecastWithoutRequiredPropertyConverterAttribute чтобы избежать бесконечного цикла. Этот подход к сериализации приводит к снижению производительности, так как создается дополнительный объект и копируются значения свойств.

Ниже приведены типы WeatherForecast*.

[JsonConverter(typeof(WeatherForecastRequiredPropertyConverterForAttributeRegistration))]
public class WeatherForecastWithRequiredPropertyConverterAttribute
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}

public class WeatherForecastWithoutRequiredPropertyConverterAttribute :
    WeatherForecastWithRequiredPropertyConverterAttribute
{
}

Ниже приведен преобразователь.

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class WeatherForecastRequiredPropertyConverterForAttributeRegistration :
        JsonConverter<WeatherForecastWithRequiredPropertyConverterAttribute>
    {
        public override WeatherForecastWithRequiredPropertyConverterAttribute Read(
            ref Utf8JsonReader reader,
            Type type,
            JsonSerializerOptions options)
        {
            // OK to pass in options when recursively calling Deserialize.
            WeatherForecastWithRequiredPropertyConverterAttribute forecast =
                JsonSerializer.Deserialize<WeatherForecastWithoutRequiredPropertyConverterAttribute>(
                    ref reader,
                    options)!;

            // Check for required fields set by values in JSON.
            return forecast!.Date == default
                ? throw new JsonException("Required property not received in the JSON")
                : forecast;
        }

        public override void Write(
            Utf8JsonWriter writer,
            WeatherForecastWithRequiredPropertyConverterAttribute forecast,
            JsonSerializerOptions options)
        {
            var weatherForecastWithoutConverterAttributeOnClass =
                new WeatherForecastWithoutRequiredPropertyConverterAttribute
                {
                    Date = forecast.Date,
                    TemperatureCelsius = forecast.TemperatureCelsius,
                    Summary = forecast.Summary
                };

            // OK to pass in options when recursively calling Serialize.
            JsonSerializer.Serialize(
                writer,
                weatherForecastWithoutConverterAttributeOnClass,
                options);
        }
    }
}

Если необходимо обрабатывать атрибуты (например, [JsonIgnore] или различные параметры (например, пользовательские кодировщики)), преобразователю свойств потребуется дополнительная логика. Кроме того, пример кода не обрабатывает свойства, для которых в конструкторе задано значение по умолчанию. И этот подход не различает следующие сценарии:

  • В JSON отсутствует свойство.
  • Свойство для типа, не допускающего значения NULL, содержится в JSON, но значение является значением по умолчанию для типа, например ноль для int.
  • Свойство для типа значения, допускающего значение NULL, содержится в JSON, но значение равно NULL.

Примечание

Если вы используете System.Text.Json из контроллера ASP.NET Core, возможно, вы сможете использовать [Required] атрибут для свойств класса модели вместо реализации преобразователяSystem.Text.Json.

Указание формата даты

Newtonsoft.Json предоставляет несколько способов управления сериализацией и десериализации свойств DateTime и типов DateTimeOffset:

  • Параметр DateTimeZoneHandling можно использовать для сериализации всех значений DateTime как даты в формате UTC.
  • Параметры DateFormatString и преобразователи DateTime можно использовать для настройки формата строк даты.

System.Text.Json поддерживает ISO 8601-1:2019, включая профиль RFC 3339. Этот формат широко используется, является однозначным и точно выполняет круговые пути. Чтобы использовать любой другой формат, создайте пользовательский преобразователь. Например, следующие преобразователи сериализуют и десериализуют JSON, в которых используется формат эпохи Unix со смещением часового пояса или без нее (такие значения, как /Date(1590863400000-0700)/ или /Date(1590863400000)/):

sealed class UnixEpochDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
    static readonly DateTimeOffset s_epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
    static readonly Regex s_regex = new Regex("^/Date\\(([+-]*\\d+)([+-])(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);

    public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {

        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime)
                || !int.TryParse(match.Groups[3].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int hours)
                || !int.TryParse(match.Groups[4].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int minutes))
        {
            throw new JsonException();
        }

        int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;
        TimeSpan utcOffset = new TimeSpan(hours * sign, minutes * sign, 0);

        return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
    }

    public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
        TimeSpan utcOffset = value.Offset;

        string formatted = FormattableString.Invariant($"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");
        writer.WriteStringValue(formatted);
    }
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
    static readonly DateTime s_epoch = new DateTime(1970, 1, 1, 0, 0, 0);
    static readonly Regex s_regex = new Regex("^/Date\\(([+-]*\\d+)\\)/$", RegexOptions.CultureInvariant);

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {

        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime))
        {
            throw new JsonException();
        }

        return s_epoch.AddMilliseconds(unixTime);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);

        string formatted = FormattableString.Invariant($"/Date({unixTime})/");
        writer.WriteStringValue(formatted);
    }
}

Дополнительные сведения см. в разделе Поддержка DateTime и DateTimeOffset в System.Text.Json.

Обратные вызовы

Newtonsoft.Json позволяет выполнять пользовательский код в нескольких точках процесса сериализации или десериализации:

  • OnDeserializing (в начале десериализации объекта)
  • OnDeserialized (после десериализации объекта)
  • OnSerializing (в начале сериализации объекта)
  • OnSerialized (после сериализации объекта)

System.Text.Json предоставляет одни и те же уведомления во время сериализации и десериализации. Чтобы использовать их, реализуйте один или несколько следующих интерфейсов из System.Text.Json.Serialization пространства имен:

Ниже приведен пример, который проверяет наличие свойства NULL и записывает сообщения в начале и конце сериализации и десериализации:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Callbacks
{
    public class WeatherForecast : 
        IJsonOnDeserializing, IJsonOnDeserialized, 
        IJsonOnSerializing, IJsonOnSerialized
    {
        public DateTime Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string? Summary { get; set; }

        void IJsonOnDeserializing.OnDeserializing() => Console.WriteLine("\nBegin deserializing");
        void IJsonOnDeserialized.OnDeserialized()
        {
            Validate();
            Console.WriteLine("Finished deserializing");
        }
        void IJsonOnSerializing.OnSerializing()
        {
            Console.WriteLine("Begin serializing");
            Validate();
        }
        void IJsonOnSerialized.OnSerialized() => Console.WriteLine("Finished serializing");

        private void Validate()
        {
            if (Summary is null)
            {
                Console.WriteLine("The 'Summary' property is 'null'.");
            }
        }
    }

    public class Program
    {
        public static void Main()
        {
            var weatherForecast = new WeatherForecast
            {
                Date = DateTime.Parse("2019-08-01"),
                TemperatureCelsius = 25,
            };

            string jsonString = JsonSerializer.Serialize(weatherForecast);
            Console.WriteLine(jsonString);

            weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString);
            Console.WriteLine($"Date={weatherForecast?.Date}");
            Console.WriteLine($"TemperatureCelsius={weatherForecast?.TemperatureCelsius}");
            Console.WriteLine($"Summary={weatherForecast?.Summary}");
        }
    }
}
// output:
//Begin serializing
//The 'Summary' property is 'null'.
//Finished serializing
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":null}

//Begin deserializing
//The 'Summary' property is 'null'.
//Finished deserializing
//Date=8/1/2019 12:00:00 AM
//TemperatureCelsius = 25
//Summary=

Код OnDeserializing не имеет доступа к новому экземпляру POCO. Чтобы управлять новым экземпляром POCO в начале десериализации, вставьте этот код в конструктор POCO.

В System.Text.Json можно имитировать обратные вызовы, написав пользовательский преобразователь. В следующем примере показан пользовательский преобразователь для POCO. Этот преобразователь включает код, отображающий сообщение в каждой точке, которая соответствует обратному вызову Newtonsoft.Json.

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class WeatherForecastCallbacksConverter : JsonConverter<WeatherForecast>
    {
        public override WeatherForecast Read(
            ref Utf8JsonReader reader,
            Type type,
            JsonSerializerOptions options)
        {
            // Place "before" code here (OnDeserializing),
            // but note that there is no access here to the POCO instance.
            Console.WriteLine("OnDeserializing");

            // Don't pass in options when recursively calling Deserialize.
            WeatherForecast forecast = JsonSerializer.Deserialize<WeatherForecast>(ref reader)!;

            // Place "after" code here (OnDeserialized)
            Console.WriteLine("OnDeserialized");

            return forecast;
        }

        public override void Write(
            Utf8JsonWriter writer,
            WeatherForecast forecast, JsonSerializerOptions options)
        {
            // Place "before" code here (OnSerializing)
            Console.WriteLine("OnSerializing");

            // Don't pass in options when recursively calling Serialize.
            JsonSerializer.Serialize(writer, forecast);

            // Place "after" code here (OnSerialized)
            Console.WriteLine("OnSerialized");
        }
    }
}

Зарегистрируйте этот пользовательский преобразователь, добавив его в коллекцию Converters.

Если вы используете пользовательский преобразователь, как в предыдущем примере:

  • Код OnDeserializing не имеет доступа к новому экземпляру POCO. Чтобы управлять новым экземпляром POCO в начале десериализации, вставьте этот код в конструктор POCO.
  • Избегайте бесконечного цикла, регистрируя преобразователь с помощью объекта options и не передавая объект options при рекурсивном вызове Serialize или Deserialize.

Дополнительные сведения о пользовательских преобразователях, которые рекурсивно вызывают Serialize или Deserialize, см. в разделе Обязательные свойства ранее в этой статье.

Методы задания и методы получения свойств, которые не являются общими

Newtonsoft.Json может использовать методы задания и получения частных и внутренних свойств с помощью атрибута JsonProperty.

System.Text.Json поддерживает методы задания и получения частных и внутренних свойств с помощью атрибута [JsonInclude]. Пример кода см. в разделе Методы доступа к свойствам, не являющимся общедоступными.

System.Text.Json в .NET Core 3.1 поддерживает только общие методы задания. Эта функция может быть предоставлена пользовательскими преобразователями.

Заполнение существующих объектов

Метод JsonConvert.PopulateObject в Newtonsoft.Json десериализует документ JSON в существующий экземпляр класса вместо создания нового экземпляра. System.Text.Json всегда создает новый экземпляр целевого типа с помощью открытого конструктора без параметров по умолчанию. Пользовательские преобразователи можно десериализовать в существующий экземпляр.

Повторное использование вместо замены свойств

Параметр Newtonsoft.JsonObjectCreationHandling позволяет указать, что объекты в свойствах следует использовать повторно, а не заменять во время десериализации. System.Text.Json всегда заменяет объекты в свойствах. Эта функция может быть предоставлена пользовательскими преобразователями.

Добавление в коллекции без методов задания

Во время десериализации Newtonsoft.Json добавляет объекты в коллекцию, даже если свойство не имеет метода задания. System.Text.Json игнорирует свойства, у которых нет методов задания. Эта функция может быть предоставлена пользовательскими преобразователями.

Политика именования обращений Snake

Единственная встроенная политика именования свойств в — System.Text.Json для верблюда. Newtonsoft.Json может преобразовывать имена свойств в регистр змеи. Пользовательская политика именования может предоставить эту функцию. Дополнительные сведения см. в статье GitHub issue dotnet/runtime #782.

Атрибуты System.Runtime.Serialization

System.Text.Json не поддерживает атрибуты из пространства имен System.Runtime.Serialization, такие как DataMemberAttribute и IgnoreDataMemberAttribute.

Числа восьмеричной системы

Newtonsoft.Json обрабатывает числа с начальным нулем как восьмеричные числа. System.Text.Json не допускает начальные нули, так как спецификация RFC 8259 не разрешает их.

MissingMemberHandling

Newtonsoft.Json можно настроить для создания исключений во время десериализации, если в JSON включены свойства, отсутствующие в целевом типе. System.Text.Json игнорирует дополнительные свойства в JSON, за исключением случаев, когда используется атрибут [JsonExtensionData]. Для функции отсутствующих членов не существует обходного решения.

TraceWriter

Newtonsoft.Json позволяет выполнять отладку с помощью TraceWriter для просмотра журналов, созданных при сериализации или десериализации. System.Text.Json не ведет журнал.

JsonDocument и JsonElement в сравнении с JToken (например, JObject, JArray)

System.Text.Json.JsonDocument предоставляет возможность выполнять синтаксический анализ и сборку модели DOM только для чтения из существующих полезных данных JSON. Модель DOM предоставляет произвольный доступ к данным в полезных данных JSON. Доступ к элементам JSON, составляющим полезные данные, можно получить с помощью типа JsonElement. Тип JsonElement предоставляет интерфейсы API для преобразования текста JSON в общие типы .NET. JsonDocument предоставляет свойство RootElement.

Начиная с .NET 6, можно анализировать и создавать изменяемую модель DOM из существующих полезных данных JSON, используя JsonNode тип и другие типы в System.Text.Json.Nodes пространстве имен. Дополнительные сведения см. в разделе Использование JsonNode.

JsonDocument является IDisposable

JsonDocument создает выполняющееся в памяти представление данных в едином буфере. Таким образом, в отличие от JObject или JArray из Newtonsoft.Json, тип JsonDocument реализует IDisposable и должен использоваться внутри блока using. Дополнительные сведения см. в разделе JsonDocument is IDisposable.

JsonDocument доступен только для чтения

Модель DOM System.Text.Json не может добавлять, удалять или изменять элементы JSON. Он разработан таким образом для повышения производительности и сокращения выделения для анализа общих размеров полезных данных JSON (т. е < . 1 МБ).

Если в вашем сценарии используется изменяемая модель DOM, возможны следующие обходные пути.

  • Чтобы создать JsonDocument с нуля (то есть без передачи существующих полезных данных JSON в метод Parse), напишите текст JSON с помощью Utf8JsonWriter и проанализируйте выходные данные, чтобы создать новый JsonDocument.
  • Чтобы изменить существующий JsonDocument, используйте его для написания текста JSON, внося изменения во время написания, и проанализируйте выходные данные для создания нового JsonDocument.
  • Сведения о слиянии существующих документов JSON, эквивалентных API JObject.Merge или JContainer.Merge из Newtonsoft.Json, см. в этой проблеме GitHub.

Эти обходные пути необходимы только для версий, предшествующих System.Text.Json 6.0. В версии 6.0 можно использовать JsonNode для работы с изменяемой моделью DOM.

JsonElement является структурой объединения

JsonDocument предоставляет в RootElement качестве свойства типа JsonElement, который представляет собой тип структуры объединения, охватывающий любой элемент JSON. Newtonsoft.Json использует выделенные иерархические типы, такие как JObject, JArray, JToken и т. д. JsonElement можно использовать для поиска и перечисления, а с помощью JsonElement можно материализовывать элементы JSON в типы .NET.

Начиная с .NET 6, в пространстве имен можно использовать JsonNode тип и типы System.Text.Json.Nodes , соответствующие JObject,JArray и JToken. Дополнительные сведения см. в разделе Использование JsonNode.

Поиск вложенных элементов в JsonDocument и JsonElement

Поиск токенов JSON с помощью JObject или JArray из Newtonsoft.Json, как правило, выполняется относительно быстро, так как они присутствуют в словаре. Для сравнения, поиск по JsonElement требует последовательного поиска свойств и, следовательно, выполняется относительно медленно (например, при использовании TryGetProperty). System.Text.Json предназначен для сокращения времени начального анализа, а не времени поиска. Дополнительные сведения см. в разделах Поиск подэлементов JsonDocument и JsonElement.

Сравнение Utf8JsonReader и JsonTextReader

System.Text.Json.Utf8JsonReader— это высокопроизводительное средство чтения JSON в кодировке UTF-8 с низким выделением, считываемое из байта> ReadOnlySpan< или байта> ReadOnlySequence<. Utf8JsonReader — это низкоуровневый тип, с помощью которого можно создавать пользовательские средства синтаксического анализа и десериализаторы.

Utf8JsonReader является структурой ссылок

Newtonsoft.Json В JsonTextReader является классом . Тип Utf8JsonReader отличается тем, что это структура ссылки. Дополнительные сведения см. в разделе Utf8JsonReader является ссылочной структурой.

Считывание значений NULL в типы значений, допускающие значения NULL

Newtonsoft.Json предоставляет API, возвращающие Nullable<T>, например ReadAsBoolean, который обрабатывает NullTokenType за вас, возвращая bool?. Встроенные API System.Text.Json возвращают только типы значений, не допускающие значения NULL. Дополнительные сведения см. в разделе Чтение значений NULL в типы значений, допускающих значение NULL.

Настройка для различных версий

Если необходимо продолжить использование Newtonsoft.Json для определенных целевых платформ, можно выбрать несколько версий и создать две реализации. Однако это нестандартный подход, и потребуется несколько #ifdefs и дублирование источника. Один из способов предоставления общего доступа к как можно большему объему кода — создать оболочку ref struct вокруг Utf8JsonReader и Newtonsoft.JsonJsonTextReader. Эта оболочка будет объединять общедоступную контактную зону при изоляции различий в поведении. Это позволяет изолировать изменения, сведя их, в основном, к созданию типа, а также передаче нового типа по ссылке. Это шаблон, которому следует библиотека Microsoft.Extensions.DependencyModel:

Сравнение Utf8JsonWriter и JsonTextWriter

System.Text.Json.Utf8JsonWriter — это высокопроизводительный способ записать текст JSON в кодировке UTF-8 из распространенных типов .NET, например String, Int32 и DateTime. Модуль записи — это низкоуровневый тип, с помощью которого можно создавать пользовательские сериализаторы.

Запись необработанных значений

Метод Newtonsoft.JsonWriteRawValue записывает необработанный код JSON, в котором ожидается значение. System.Text.Json имеет прямой эквивалент: Utf8JsonWriter.WriteRawValue. Дополнительные сведения см. в разделе Запись необработанного JSON.

Метод Newtonsoft.JsonWriteRawValue записывает необработанный код JSON, в котором ожидается значение. В .NET 6 и более поздних версиях существует эквивалентный метод Utf8JsonWriter.WriteRawValue. Дополнительные сведения см. в разделе Запись необработанного JSON.

Для версий, предшествующих 6.0, System.Text.Json не имеет эквивалентного метода записи необработанного JSON. Однако следующий обходной путь гарантирует, что записывается только допустимый JSON:

using JsonDocument doc = JsonDocument.Parse(string);
doc.WriteTo(writer);

Настройка формата JSON

JsonTextWriter включает следующие параметры, для которых Utf8JsonWriter не имеет эквивалента:

  • Отступ — задает количество символов для отступа. Utf8JsonWriter всегда имеет отступы на 2 символа.
  • IndentChar — указывает символ, используемый для отступа. Utf8JsonWriter всегда использует пробелы.
  • QuoteChar — указывает символ, используемый для заключения строковых значений. Utf8JsonWriter всегда использует двойные кавычки.
  • QuoteName — указывает, следует ли заключать имена свойств в кавычки. Utf8JsonWriter всегда заключает их в кавычки.

Не существует обходных решений, которые позволяют настроить JSON, созданный Utf8JsonWriter такими методами.

Запись значений TimeSpan, URI или char

JsonTextWriter предоставляет методы WriteValue для значений TimeSpan, URI и char. Utf8JsonWriter не имеет эквивалентных методов. Вместо этого следует отформатировать эти значения как строки (например, вызвав ToString()) и вызвать WriteStringValue.

Настройка для различных версий

Если необходимо продолжить использование Newtonsoft.Json для определенных целевых платформ, можно выбрать несколько версий и создать две реализации. Однако это нестандартный подход, и потребуется несколько #ifdefs и дублирование источника. Один из способов предоставления общего доступа к как можно большему объему кода — создать оболочку вокруг Utf8JsonWriter и NewtonsoftJsonTextWriter. Эта оболочка будет объединять общедоступную контактную зону при изоляции различий в поведении. Это позволяет изолировать изменения и свести их, в основном, к созданию типа. Библиотека Microsoft.Extensions.DependencyModel следует:

TypeNameHandling.All не поддерживается

Решение исключить TypeNameHandling.Allиз функции, эквивалентной System.Text.Json функции, было принято намеренно. Предоставление полезных данных JSON для указания сведений о собственном типе является распространенным источником уязвимостей в веб-приложениях. В частности, настройка Newtonsoft.Json с TypeNameHandling.All позволяет удаленному клиенту внедрять все исполняемое приложение в сами полезные данные JSON, чтобы во время десериализации веб-приложение извлекает и запускает внедренный код. Дополнительные сведения см. в разделах Friday the 13th JSON attacks PowerPoint and Friday the The Friday the 13th JSON attacks details.

Запросы пути JSON не поддерживаются

Модель JsonDocument DOM не поддерживает запросы с помощью пути JSON.

JsonNode В модели DOM каждый JsonNode экземпляр имеет GetPath метод, который возвращает путь к узлу. Но нет встроенного API для обработки запросов на основе строк запроса ПУТИ JSON.

Дополнительные сведения см. в описании проблемы с dotnet/runtime #31068 на GitHub.

Некоторые ограничения не настраиваемы

System.Text.Json задает ограничения, которые нельзя изменить для некоторых значений, таких как максимальный размер маркера в символах (166 МБ) и базовый размер 64 (125 МБ). Дополнительные сведения см JsonConstants . в исходном коде и проблеме GitHub dotnet/runtime #39953.

NaN, Бесконечность, -Infinity

Newtonsoft анализирует строковые токены NaN, Infinityи -Infinity JSON. В .NET Core 3.1 не поддерживает эти маркеры, System.Text.Json но для их обработки можно написать пользовательский преобразователь. В .NET 5 и более поздних версиях используйте JsonNumberHandling.AllowNamedFloatingPointLiterals. Сведения об использовании этого параметра см. в разделе Разрешить или записывать числа в кавычки.

Дополнительные ресурсы