Сравните 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 предоставляет следующие способы игнорирования свойств или полей при сериализации:
- Атрибут [JsonIgnore] в свойстве приводит к исключению свойства из JSON во время сериализации.
- Глобальный параметр IgnoreReadOnlyProperties позволяет игнорировать все свойства "только для чтения".
- Если вы включаете поля, глобальный параметр JsonSerializerOptions.IgnoreReadOnlyFields позволит игнорировать все поля, предназначенные только для чтения.
- Глобальный параметр
DefaultIgnoreCondition
позволяет игнорировать все свойства типа значения, имеющие значения по умолчанию, или игнорировать все свойства ссылочного типа, имеющие значения NULL.
Кроме того, в .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
не поддерживаются:
Дополнительные сведения см. в разделе Сохранение ссылок и обработка циклических ссылок.
- JsonPropertyAttribute.IsReference
- JsonPropertyAttribute.ReferenceLoopHandling
- JsonSerializerSettings.ReferenceLoopHandling
Дополнительные сведения см. в разделе Сохранение ссылок и обработка циклических ссылок.
System.Text.Json в .NET Core 3.1 поддерживает сериализацию только по значению и вызывает исключение для циклических ссылок.
Словарь с ключом, не являющимся строкой
И Newtonsoft.Json
, и System.Text.Json
поддерживают коллекции типа Dictionary<TKey, TValue>
. Однако в System.Text.Json
TKey
должен быть примитивным типом, а не пользовательским типом. Дополнительные сведения см. в разделе Поддерживаемые типы ключей.
Внимание!
Десериализация в , 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 не предоставляет встроенную поддержку для следующих типов:
- DataTable и связанные типы (дополнительные сведения см. в разделе Поддерживаемые типы коллекций)
- Типы F#, такие как различаемые объединения. Типы записей и анонимные типы записей рассматриваются как неизменяемые POCOs и поэтому поддерживаются.
- TimeSpan
- Типы F#, такие как различаемые объединения, типы записей и типы анонимных записей.
- TimeSpan
- ExpandoObject
- TimeZoneInfo
- BigInteger
- DBNull
- Type
- ValueTuple и связанные с ним универсальные типы
Пользовательские преобразователи можно реализовать для типов, у которых нет встроенной поддержки.
Полиморфная сериализация
Newtonsoft.Json
автоматически выполняет полиморфную сериализацию. Начиная с .NET 7, System.Text.Json поддерживает полиморфную сериализацию с помощью атрибута JsonDerivedTypeAttribute . Дополнительные сведения см. в разделе Сериализация свойств производных классов.
Полиморфная десериализация
Newtonsoft.Json
TypeNameHandling
имеет параметр , который добавляет метаданные имени типа в 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.Json
— JsonSerializerOptions.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
свойства, выберите один из следующих параметров:
- Используйте .NET 7 или более позднюю версию System.Text.Json пакета и добавьте в свойство модификатор
required
(доступен начиная с C# 11) или JsonRequiredAttribute атрибут . - Реализуйте пользовательский преобразователь.
- Реализация обратного
OnDeserialized
вызова (.NET 6 и более поздних версий).
Следующий пример кода преобразователя создает исключение, если после десериализации свойство 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.Json
ObjectCreationHandling
позволяет указать, что объекты в свойствах следует использовать повторно, а не заменять во время десериализации. 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
, который обрабатывает Null
TokenType
за вас, возвращая bool?
. Встроенные API System.Text.Json
возвращают только типы значений, не допускающие значения NULL. Дополнительные сведения см. в разделе Чтение значений NULL в типы значений, допускающих значение NULL.
Настройка для различных версий
Если необходимо продолжить использование 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.Json
WriteRawValue
записывает необработанный код JSON, в котором ожидается значение. System.Text.Json имеет прямой эквивалент: Utf8JsonWriter.WriteRawValue. Дополнительные сведения см. в разделе Запись необработанного JSON.
Метод Newtonsoft.Json
WriteRawValue
записывает необработанный код 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
и Newtonsoft
JsonTextWriter
. Эта оболочка будет объединять общедоступную контактную зону при изоляции различий в поведении. Это позволяет изолировать изменения и свести их, в основном, к созданию типа. Библиотека 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. Сведения об использовании этого параметра см. в разделе Разрешить или записывать числа в кавычки.
Дополнительные ресурсы
- Общие сведения о System.Text.Json
- Практическое руководство. Сериализация и десериализация JSON
- Создание экземпляров JsonSerializerOptions
- Сопоставление без учета регистра
- Настройка имен и значений свойств
- Игнорирование свойств
- Применение недействительного кода JSON
- Обработка переполнения JSON или использование JsonElement или JsonNode
- Сохранение ссылок и обработка циклических ссылок
- Десериализация в неизменяемые типы и методы доступа, не являющиеся открытыми
- Полиморфная сериализация
- Настройка кодировки символов
- Использование DOM, Utf8JsonReader и Utf8JsonWriter
- Написание пользовательских преобразователей для сериализации JSON
- Поддержка DateTime и DateTimeOffset
- Как использовать создание источника
- Поддерживаемые типы коллекций
- Справочник по API System.Text.Json
- Справочник по API System.Text.Json.Serialization