Поделиться через


Переход из Newtonsoft.JsonSystem.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.6.2 и более поздние версии
  • .NET Core 2.0, 2.1 и 2.2

Совет

Вы можете использовать помощь ИИ для миграции с Newtonsoft.Json помощью GitHub Copilot.

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

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

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

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

Таблица различий

В следующей таблице перечислены функции 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, настраиваемая ✔️ Максимальная глубина по умолчанию —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]
Десериализация значения перечисления строки ✔️ Десериализация строковых значений перечисления
Глобальный параметр MissingMemberHandling ✔️ Обработка отсутствующих элементов
Заполнение свойств без наборов ✔️ Заполнение свойств без наборов
Глобальный параметр ObjectCreationHandling ✔️ Повторное использование, а не замена свойств
Поддержка широкого спектра типов ⚠️ Для некоторых типов требуются пользовательские преобразователи
Десериализация выводимого типа в свойства object ⚠️ Не поддерживается, обходное решение, пример
Десериализация литерала JSON null в типы значений, не допускающие значения NULL ⚠️ Не поддерживается, обходное решение, пример
Параметры DateTimeZoneHandling, DateFormatString ⚠️ Не поддерживается, обходное решение, пример
JsonConvert.PopulateObjectМетод ⚠️ Не поддерживается, обходной путь
Поддержка атрибутов System.Runtime.Serialization ⚠️ Не поддерживается, обходное решение, пример
JsonObjectAttribute ⚠️ Не поддерживается, обходной путь
Возможность имен свойств без кавычек Не поддерживается проектом
Возможность одиночных кавычек вокруг строковых значений Не поддерживается проектом
Возможность нестроковых значений JSON для строковых свойств Не поддерживается проектом
Глобальный параметр TypeNameHandling.All Не поддерживается проектом
JsonPath Поддержка запросов Не поддерживаются
Настраиваемые ограничения Не поддерживаются

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

Различия в поведении по умолчанию

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 [JsonConstructor] позволяет указать, какой конструктор вызывать при десериализации в POCO.

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

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

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

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

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

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

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

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

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

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

По умолчанию 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.JsonКак и в ReferenceResolver, System.Text.Json.Serialization.ReferenceResolver класс определяет поведение сохранения ссылок на сериализацию и десериализацию. Создайте производный класс, чтобы указать настраиваемое поведение. Пример см. в статье GuidReferenceResolver.

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

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

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

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

Внимание

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

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

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

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

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

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

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

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

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

Десериализация строковых значений перечисления

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

string json = "{ \"Text\": \"Hello\", \"Enum\": \"Two\" }";
var _ = JsonSerializer.Deserialize<MyObj>(json); // Throws exception.

class MyObj
{
    public string Text { get; set; } = "";
    public MyEnum Enum { get; set; }
}

enum MyEnum
{
    One,
    Two,
    Three
}

Однако можно включить десериализацию строковых значений перечисления с помощью преобразователя JsonStringEnumConverter . Дополнительные сведения см. в разделе Перечисления в виде строк.

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

Когда 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 для POCOs, которые указывают значения по умолчанию. Например, следующий код представляет целевой объект:

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 также являются неизменяемыми и поддерживаются в качестве целей десериализации. Дополнительные сведения см. в разделе Неизменяемые типы и записи.

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

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

Начиная с .NET 7, можно использовать модификатор C# required или JsonRequiredAttribute атрибут в обязательном свойстве. System.Text.Json вызывает исключение, если полезные данные 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(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
    static readonly Regex s_regex = new("^/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(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 = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");

        writer.WriteStringValue(formatted);
    }
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
    static readonly DateTime s_epoch = new(1970, 1, 1, 0, 0, 0);
    static readonly Regex s_regex = new("^/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 = string.Create(CultureInfo.InvariantCulture, $"/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.

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

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

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

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

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

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

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

Дополнительные сведения см. в разделе "Заполнение инициализированных свойств".

Заполнение свойств без наборов

Начиная с .NET 8, System.Text.Json поддерживает заполнение свойств, включая те, у которых нет набора. Дополнительные сведения см. в разделе "Заполнение инициализированных свойств".

Политика именования регистра змеи

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

Входные данные Newtonsoft.Json результат System.Text.Json результат
AB1 "a_b1" Ab1
"SHA512Managed" "sh_a512_managed" "sha512_managed"
"abc123DEF456" "abc123_de_f456" "abc123_def456"
"KEBAB-CASE" "keba_b-_case" "kebab-case"

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

System.Runtime.Serialization такие атрибуты, как DataContractAttribute, DataMemberAttributeи IgnoreDataMemberAttribute позволяют определить контракт данных. Контракт данных - формальное соглашение между службой и клиентом, абстрактно описывающее данные, обмен которыми происходит. Контракт данных точно определяет, какие свойства сериализуются для обмена.

System.Text.Json не поддерживает встроенные атрибуты. Однако начиная с .NET 7 можно использовать настраиваемый сопоставитель типов для добавления поддержки. Пример см. в разделе ZCS. DataContractResolver.

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

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

Обработка отсутствующих элементов

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

В .NET 8 и более поздних версиях можно настроить выбор того, следует ли пропускать или запрещать несопоставленные свойства JSON с помощью одного из следующих средств:

JsonObjectAttribute

Newtonsoft.Json имеет атрибут, JsonObjectAttributeкоторый можно применить на уровне типа для управления сериализующимися элементами, способами null обработки значений и необходимости всех элементов. System.Text.Json не имеет эквивалентного атрибута, который можно применить к типу. Для некоторых действий, таких как null обработка значений, можно настроить одно и то же поведение в глобальном JsonSerializerOptions или отдельном свойстве.

Рассмотрим следующий пример, который используется Newtonsoft.Json.JsonObjectAttribute для указания того, что все null свойства должны игнорироваться:

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class Person { ... }

В System.Text.Jsonэтом случае можно задать поведение для всех типов и свойств:

JsonSerializerOptions options = new()
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

string json = JsonSerializer.Serialize<Person>(person, options);

Или можно задать поведение для каждого свойства отдельно:

public class Person
{
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Name { get; set; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public int? Age { get; set; }
}

Далее рассмотрим следующий пример, который используется Newtonsoft.Json.JsonObjectAttribute для указания того, что все свойства члена должны присутствовать в JSON:

[JsonObject(ItemRequired = Required.Always)]
public class Person { ... }

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

public class Person
{
    [JsonRequired]
    public string? Name { get; set; }

    public required int? Age { get; set; }
}

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 — IDisposable.

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

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

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

JsonDocument предоставляет RootElement свойство типа JsonElement, которое является типом структуры объединения, которая охватывает любой элемент JSON. Newtonsoft.Jsonиспользует выделенные иерархические типы, такие как JObject, JArrayJTokenи т. д. 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 отличается в том, что это структура ссылок. Дополнительные сведения см. в разделе об ограничениях структуры ref для Utf8JsonReader.

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

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

Многоцелевой объект для чтения JSON

Если необходимо продолжить использование Newtonsoft.Json для определенных целевых платформ, можно выбрать несколько версий и создать две реализации. Однако это нестандартный подход, и потребуется несколько #ifdefs и дублирование источника. Одним из способов совместного использования максимально возможного объема кода является создание оболочки вокруг ref struct и Utf8JsonReader Newtonsoft.Json.JsonTextReader. Эта оболочка будет объединять общедоступную контактную зону при изоляции различий в поведении. Это позволяет изолировать изменения, сведя их, в основном, к созданию типа, а также передаче нового типа по ссылке. Это шаблон, которому следует библиотека 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".

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

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

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

Начиная с .NET 9, можно настроить символ отступа и размер для Utf8JsonWriter использования параметров, предоставляемых структурой JsonWriterOptions :

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

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

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

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

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

Многоцелевой объект для записи JSON

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

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

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

Запросы пути 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, Бесконечность, -Бесконечность

Синтаксический NaNInfinityанализ Newtonsoft и -Infinity токены строк JSON. С System.Text.Jsonпомощью , используйте JsonNumberHandling.AllowNamedFloatingPointLiterals. Сведения об использовании этого параметра см. в разделе "Разрешить" или "Записать номера" в кавычках.

Миграция с помощью GitHub Copilot

Вы можете получить справку по программированию из GitHub Copilot для преобразования кода в интегрированной среде разработки.

Если вы используете Visual Studio 2022 версии 17.8 или более поздней, вы можете попробовать GitHub Copilot на основе ИИ в Visual Studio , чтобы перенести Newtonsoft.Json код на использование System.Text.Json. Отправьте свой вопрос и код для преобразования в виде запроса в окне чата copilot, как показано в следующем примере.

Примечание.

GitHub Copilot работает СИ, поэтому возможны сюрпризы и ошибки. Обязательно проверьте любой созданный код или предложения. Дополнительные сведения об общем использовании GitHub Copilot, влиянии на продукты, надзоре и конфиденциальности см . в часто задаваемых вопросы о GitHub Copilot.

Пример запроса для Чата Copilot

convert the following code to use System.Text.Json
Product product = new Product();

product.Name = "Apple";
product.ExpiryDate = new DateTime(2024, 08, 08);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };

string output = JsonConvert.SerializeObject(product);
Console.WriteLine(output);

Пример ответа Copilot Chat

To convert the code to use System.Text.Json, you can use the JsonSerializer class. Here's the updated code:
using System;
using System.Text.Json;

public class Product
{
    public string Name { get; set; }
    public DateTime ExpiryDate { get; set; }
    public decimal Price { get; set; }
    public string[] Sizes { get; set; }
}

public class Program
{
    public static void Main()
    {
        Product product = new Product();

        product.Name = "Apple";
        product.ExpiryDate = new DateTime(2024, 08, 08);
        product.Price = 3.99M;
        product.Sizes = new string[] { "Small", "Medium", "Large" };

        string output = JsonSerializer.Serialize(product);
        Console.WriteLine(output);
    }
}
This code uses System.Text.Json.JsonSerializer.Serialize to convert the Product object to a JSON string.

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

Анимированный снимок экрана, на котором показано использование чата GitHub Copilot в Visual Studio для миграции из Newtonsoft

Вы можете использовать функции чата, такие как команды косой черты, ссылки и потоки, чтобы задать намерение и получить лучшие ответы с контекстом области. Например, если файл filename кода открыт в интегрированной среде разработки, вы можете ссылаться на файл в запросе на Copilot Chat с параметром "преобразовать #filename в использование System.Text.Json". Вы также можете ссылаться на решение с помощью "преобразовать @workspace в использование System.Text.Json" в окне чата или в встроенном чате.

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