Verification: a143cc29221c9be0

Parse json file with php

Parse json file with php

Таблица различий между 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]
Поддержка широкого спектра типов ⚠️ Для некоторых типов требуются пользовательские преобразователи
Полиморфная сериализация ⚠️ Не поддерживается, существует обходной путь, пример
Полиморфная десериализация ⚠️ Не поддерживается, существует обходной путь, пример
Десериализация выводимого типа в свойства object ⚠️ Не поддерживается, существует обходной путь, пример
Десериализация литерала JSON null в типы значений, не допускающие значения NULL ⚠️ Не поддерживается, существует обходной путь, пример
Установка Required для атрибута [JsonProperty] ⚠️ Не поддерживается, существует обходной путь, пример
DefaultContractResolver для игнорирования свойств ⚠️ Не поддерживается, существует обходной путь, пример
Параметры DateTimeZoneHandling, DateFormatString ⚠️ Не поддерживается, существует обходной путь, пример
Обратные вызовы ⚠️ Не поддерживается, существует обходной путь, пример
Метод JsonConvert.PopulateObject ⚠️ Не поддерживается, существует обходной путь, пример
Глобальный параметр ObjectCreationHandling ⚠️ Не поддерживается, существует обходной путь, пример
Добавление в коллекции без методов задания ⚠️ Не поддерживается, существует обходной путь, пример
Глобальный параметр ReferenceLoopHandling ❌ Не поддерживается
Поддержка атрибутов System.Runtime.Serialization ❌ Не поддерживается
Глобальный параметр MissingMemberHandling ❌ Не поддерживается
Возможность имен свойств без кавычек ❌ Не поддерживается
Возможность одиночных кавычек вокруг строковых значений ❌ Не поддерживается
Возможность нестроковых значений JSON для строковых свойств ❌ Не поддерживается
Функция 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 ⚠️ Не поддерживается, существует обходной путь, пример
Добавление в коллекции без методов задания ⚠️ Не поддерживается, существует обходной путь, пример
Глобальный параметр PreserveReferencesHandling ❌ Не поддерживается
Глобальный параметр ReferenceLoopHandling ❌ Не поддерживается
Поддержка атрибутов System.Runtime.Serialization ❌ Не поддерживается
Глобальный параметр MissingMemberHandling ❌ Не поддерживается
Возможность имен свойств без кавычек ❌ Не поддерживается
Возможность одиночных кавычек вокруг строковых значений ❌ Не поддерживается
Возможность нестроковых значений JSON для строковых свойств ❌ Не поддерживается

Это неполный список функций 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.

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

Во время сериализации 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 по умолчанию не имеет максимального предела глубины. Для 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 в .NET Core 3.1, реализуйте пользовательский преобразователь, как показано в следующем примере. Преобразователь обрабатывает свойства, определенные как long:

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

namespace SystemTextJsonSamples
{
    public class LongToStringConverter : JsonConverter
    {
        public override long Read(
            ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.String)
            {
                ReadOnlySpan 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.

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

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

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

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

  • Атрибут [JsonIgnore] в свойстве приводит к исключению свойства из JSON во время сериализации.
  • Глобальный параметр IgnoreNullValues позволяет игнорировать все свойства со значением NULL.
  • Глобальный параметр IgnoreReadOnlyProperties позволяет игнорировать все свойства "только для чтения".

Эти параметры не позволяют:

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

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

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

namespace SystemTextJsonSamples
{
    public class WeatherForecastRuntimeIgnoreConverter : JsonConverter
    {
        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 в .NET Core 3.1 работает только с общими свойствами. Эта функция может быть предоставлена пользовательскими преобразователями.

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

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

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

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

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

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

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

И Newtonsoft.Json, и System.Text.Json поддерживают коллекции типа Dictionary.

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

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

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

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

  • DataTable и связанные типы
  • Типы F#, такие как различаемые объединения, типы записей и типы анонимных записей.
  • ExpandoObject
  • TimeZoneInfo
  • BigInteger
  • TimeSpan
  • DBNull
  • Type
  • ValueTuple и связанные с ним универсальные типы

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

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

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

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

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

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

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

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

Когда 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;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class DateTimeOffsetNullHandlingConverter : JsonConverter
    {
        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;
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;

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

        private readonly JsonConverter _intConverter;

        public ImmutablePointConverter(JsonSerializerOptions options) => 
            _intConverter = options?.GetConverter(typeof(int)) is JsonConverter 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 не получено значение для свойства, помеченного как обязательное.

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

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

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

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

Чтобы десериализация завершалась ошибкой, когда в JSON отсутствуют свойства Date, реализуйте пользовательский преобразователь. Следующий пример кода преобразователя создает исключение, если после десериализации свойство Date не задано:

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

namespace SystemTextJsonSamples
{
    public class WeatherForecastRequiredPropertyConverter : JsonConverter
    {
        public override WeatherForecast Read(
            ref Utf8JsonReader reader,
            Type type,
            JsonSerializerOptions options)
        {
            // Don't pass in options when recursively calling Deserialize.
            WeatherForecast forecast = JsonSerializer.Deserialize(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;
using System.Text.Json;
using System.Text.Json.Serialization;

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

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

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

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

System.Text.Json поддерживает ISO 8601-1:2019, включая профиль RFC 3339. Этот формат широко используется, является однозначным и точно выполняет круговые пути. Чтобы использовать любой другой формат, создайте пользовательский преобразователь. Дополнительные сведения см. в разделе Поддержка DateTime и DateTimeOffset в System.Text.Json.

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

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

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

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

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

namespace SystemTextJsonSamples
{
    public class WeatherForecastCallbacksConverter : JsonConverter
    {
        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(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 в .NET Core 3.1 поддерживает только общие методы задания. Эта функция может быть предоставлена пользовательскими преобразователями.

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

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

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

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

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

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

Атрибуты 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.

JsonDocument является IDisposable

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

Возвращайте JsonDocument из API только в том случае, если хотите передать владение временем существования и ответственность вызывающему объекту. В большинстве случаев это необязательно. Если вызывающему объекту необходимо работать со всем документом JSON, возвращайте Clone RootElement, то есть JsonElement. Если вызывающему объекту необходимо работать с определенным элементом в документе JSON, возвращайте Clone этого JsonElement. Если вы возвращаете RootElement или вложенный элемент напрямую, не выполняя Clone, вызывающий объект не сможет получить доступ к возвращаемому объекту JsonElement после того, как JsonDocument, которому он принадлежит, будет удален.

Ниже приведен пример, в котором необходимо сделать Clone.

public JsonElement LookAndLoad(JsonElement source)
{
    string json = File.ReadAllText(source.GetProperty("fileName").GetString());

    using (JsonDocument doc = JsonDocument.Parse(json))
    {
        return doc.RootElement.Clone();
    }
}

Приведенный выше код ждет JsonElement, содержащий свойство fileName. Он открывает JSON-файл и создает JsonDocument. Метод предполагает, что вызывающий объект хочет работать со всем документом, поэтому он возвращает Clone RootElement.

Если вы получаете JsonElement и возвращаете вложенный элемент, нет необходимости возвращать Clone вложенного элемента. Вызывающий объект отвечает за поддержание активности JsonDocument, которому принадлежит переданный JsonElement. Пример:

public JsonElement ReturnFileName(JsonElement source)
{
   return source.GetProperty("fileName");
}

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

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

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

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

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

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

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

  • Используйте встроенные перечислители (EnumerateArray и EnumerateObject) вместо создания собственных индексов или циклов.
  • Не выполняйте последовательный поиск по всему JsonDocument в каждом свойстве с помощью RootElement. Вместо этого выполните поиск по вложенным объектам JSON на основе известной структуры данных JSON. Например, если вы ищете свойство Grade в объектах Student, пройдите по объектам Student и получите значение Grade для каждого из них, вместо того, чтобы проходить по всем объектам JsonElement в поисках свойств Grade. Последний метод приведет к ненужным проходам по тем же данным.

Пример кода см. в разделе Использование JsonDocument для доступа к данным.

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

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

В следующих разделах описываются рекомендуемые шаблоны программирования для использования Utf8JsonReader.

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

Поскольку тип Utf8JsonReader является структурой ссылок, он имеет определенные ограничения. Например, он не может храниться как поле в классе или структуре, отличной от структуры ссылки. Для достижения высокой производительности этот тип должен быть ref struct, поскольку ему необходимо кэшировать входной объект ReadOnlySpan, который сам по себе является структурой ссылок. Кроме того, этот тип является изменяемым, так как имеет состояние. Поэтому передавайте его по ссылке, а не по значению. Передача его по значению приведет к копированию структуры, и изменения состояния не будут видны вызывающему объекту. Это отличается от Newtonsoft.Json, так как Newtonsoft.Json JsonTextReader является классом. Дополнительные сведения об использовании структур ссылок см. в статье Написание безопасного и эффективного кода C#.

Чтение текста UTF-8

Для достижения наилучшей производительности при использовании Utf8JsonReader читайте полезные данные JSON, уже закодированные как текст UTF-8, а не как строки UTF-16. Пример кода см. в разделе Фильтрация данных с помощью Utf8JsonReader.

Чтение с помощью Stream или PipeReader

Utf8JsonReader поддерживает чтение ReadOnlySpan или ReadOnlySequence в кодировке UTF-8 (результат чтения из PipeReader).

Для синхронного чтения можно считывать полезные данные JSON до конца потока в массив байтов и передать его в модуль чтения. Для чтения из строки (в кодировке UTF-16) вызовите UTF8.GetBytes, чтобы сначала перекодировать строку в массив байтов в кодировке UTF-8. Затем передайте данные в Utf8JsonReader.

Поскольку Utf8JsonReader считает входные данные текстом JSON, метка порядка байтов UTF-8 считается недопустимым JSON. Вызывающий объект должен использовать фильтр, прежде чем передавать данные в модуль чтения.

Примеры кода см. в разделе Использование Utf8JsonReader.

Чтение с помощью ReadOnlySequence с несколькими сегментами

Если входные данные JSON являются ReadOnlySpan, к каждому элементу JSON можно получить доступ из свойства ValueSpan в модуле чтения при проходе цикла чтения. Однако если входными данными является ReadOnlySequence (результат чтения из PipeReader), некоторые элементы JSON могут создать несколько сегментов объекта ReadOnlySequence. Эти элементы не будут доступны из ValueSpan в непрерывном блоке памяти. Вместо этого каждый раз, когда у вас есть ReadOnlySequence с несколькими сегментами в качестве входных данных, следует опросить свойство HasValueSequence в модуле чтения, чтобы выяснить, как получить доступ к текущему элементу JSON. Вот рекомендуемый шаблон:

while (reader.Read())
{
    switch (reader.TokenType)
    {
        // ...
        ReadOnlySpan jsonElement = reader.HasValueSequence ?
            reader.ValueSequence.ToArray() :
            reader.ValueSpan;
        // ...
    }
}

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

Не используйте ValueSpan для побайтового сравнения путем вызова SequenceEqual для поиска имени свойства. Вместо этого вызовите ValueTextEquals, так как этот метод отменяет экранирование всех символов, которые экранированы в JSON. Ниже приведен пример, демонстрирующий поиск свойства с именем name:

private static readonly byte[] s_nameUtf8 = Encoding.UTF8.GetBytes("name");
while (reader.Read())
{
    switch (reader.TokenType)
    {
        case JsonTokenType.StartObject:
            total++;
            break;
        case JsonTokenType.PropertyName:
            if (reader.ValueTextEquals(s_nameUtf8))
            {
                count++;
            }
            break;
    }
}

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

Newtonsoft.Json предоставляет API, которые возвращают Nullable, например ReadAsBoolean, который обрабатывает Null TokenType, возвращая bool?. Встроенные API System.Text.Json возвращают только типы значений, не допускающие значения NULL. Например, Utf8JsonReader.GetBoolean возвращает bool. Он вызывает исключение, если обнаруживает Null в JSON. В следующих примерах показаны два способа обработки значений NULL: возврат типа значения, допускающего значение NULL, и возврат значения по умолчанию:

public bool? ReadAsNullableBoolean()
{
    _reader.Read();
    if (_reader.TokenType == JsonTokenType.Null)
    {
        return null;
    }
    if (_reader.TokenType != JsonTokenType.True && _reader.TokenType != JsonTokenType.False)
    {
        throw new JsonException();
    }
    return _reader.GetBoolean();
}
public bool ReadAsBoolean(bool defaultValue)
{
    _reader.Read();
    if (_reader.TokenType == JsonTokenType.Null)
    {
        return defaultValue;
    }
    if (_reader.TokenType != JsonTokenType.True && _reader.TokenType != JsonTokenType.False)
    {
        throw new JsonException();
    }
    return _reader.GetBoolean();
}

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

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

  • UnifiedJsonReader.JsonTextReader.cs
  • UnifiedJsonReader.Utf8JsonReader.cs

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

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

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

Запись с помощью текста UTF-8

Для достижения наилучшей производительности при использовании Utf8JsonWriter записывайте полезные данные JSON, уже закодированные как текст UTF-8, а не как строки UTF-16. Используйте JsonEncodedText, чтобы кэшировать и предварительно закодировать известные имена и значения свойств строки как статические, а затем передать их в модуль записи вместо использования строковых литералов UTF-16. Это быстрее, чем кэширование и использование байтовых массивов UTF-8.

Этот подход также работает, если необходимо выполнить пользовательское экранирование. System.Text.Json не позволяет отключить экранирование при записи строки. Однако можно передать собственный пользовательский JavaScriptEncoder в качестве параметра для модуля записи или создать собственный JsonEncodedText, который использует JavascriptEncoder для выполнения экранирования, а затем написать JsonEncodedText вместо строки. Дополнительные сведения см. в статье Настройка кодировки символов.

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

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

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

Настройка экранирования символов

Параметр StringEscapeHandling JsonTextWriter предлагает варианты для экранирования всех символов, не являющихся символами ASCII, или символов HTML. По умолчанию Utf8JsonWriter экранирует все символы, отличные от ASCII, и символы HTML. Такое экранирование выполняется в целях глубокой защиты. Чтобы указать другую политику экранирования, создайте JavaScriptEncoder и задайте JsonWriterOptions.Encoder. Дополнительные сведения см. в статье Настройка кодировки символов.

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

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

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

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

Запись значений NULL

Чтобы записать значения NULL с помощью Utf8JsonWriter, вызовите:

  • WriteNull для записи пары "ключ-значение" со значением NULL.
  • WriteNullValue для записи значения NULL в качестве элемента массива JSON.

Для строкового свойства, если строка имеет значение NULL, WriteString и WriteStringValue эквивалентны WriteNull и WriteNullValue.

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

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

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

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

  • UnifiedJsonWriter.JsonTextWriter.cs
  • UnifiedJsonWriter.Utf8JsonWriter.cs

Что такое YML?

YML (Yandex Market Language) - это стандарт, разработанный Яндексом для принятия и размещения информации в базе данных Яндекс.Маркета. YML основан на стандарте XML.

Настройки формата:

Формировать offer id из — позволяет выбрать способ формирования атрибута id у тега offer, определяющего ID товарного предложения.

Разделять мультисвойства — позволяет выбрать способ разделения выбираемых свойств (Размеры, Цвета и т.п.): либо на основе повторяющегося тега param, либо на основе разбиения предложения на отдельные товары на group_id, согласно спецификации YML.

Товары не в наличии — можно задать как будут отображаться товары "Не в наличии" в маркете: Под заказ, т.е. с возможностью заказать или полностью не в наличии.

Общие настройки:

Выгружать товары — позволяет выбрать какие товары выгружать по признаку "Наличие" на сайте поставщика.

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

Разрешить HTML разметку в полях товара — разрешает или запрещает HTML-разметку в полях товара. Очень редко используется интернет-магазинами.

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

Выгрузка характеристик — позволяет выгрузить свойства товаров (цвета, размеры и пр.) отдельными полями в файле или просто добавить к общему описанию товара. При добавлении к описанию сами колонки остаются. Выбирается в зависимости от возможностей вашего интернет-магазина или сайта СП.

Разбить на несколько файлов — позволяет разбить выгрузку на несколько файлов: по категориям или по брендам.

Нашли ошибку в выгрузке в этот формат?

Если вы обнаружили ошибку в формате выгрузки Яндекс.Маркет (YML), пожалуйста, сообщите нам на или в чат на сайте. Мы постараемся исправить выгрузку как можно скорее.

Плагин позволяет импортировать товары из других магазинов через Yandex XML feed, который используется магазинами для торговли на Яндекс.Маркете.
Товары импортируются в структуру плагина магазина WP Shop. Работает автоматическая синхронизация товаров с источником, которую можно запускать как вручную, так и через крон.
Незаменимый инструмент для:
1. Переноса магазина с любых других движков на WordPress WP-Shop
2. Построения партнерских магазинов, для зарабатывания на партнерской комиссии по модели CPS

Для работы приложения требуется IonCube Loader!

Arbitrary section 1

Часто задаваемые вопросы

Installation Instructions

  1. Upload plugin «WP Shop YML Parser» to the /wp-content/plugins/ directory
  2. Activate the plugin «WP Shop YML Parser» through the ‘Plugins’ menu in WordPress
  3. See full userguide how to set up your «WP Shop YML Parser»

A question that someone might have

Visit the site wp-shop.ru for help.

Отзывы

Ни в коем случае не связывайтесь с этими разработчиками и ничего у них не покупайте. Тот функционал, который они обещают в про версии - обман. Они просто возьмут с вас деньги, а потом от всего откажутся, в том числе от поддержки. Просто посмотрите их кривой сайт и полуживую документацию и сами всё поймёте!

Журнал изменений

Version: 0.9
-project_as_field
-id_as_field

Version: 0.8
-template_price (custom price tag)

Version: 0.7
-fields_update — new setting to update custom fields in projects

Version: 0.6
-Sample xml parser replaced by SAX parser that better for memory management

Version: 0.5
-improovments

Version: 0.4
-bulk analizing
-clone project by category

Version: 0.3
-link to docs

Version: 0.2
-local feeds enable
— source as file enable
— addition yml options

Version: 0.1
-initial relese

Испытание приборов, файлы конфигурации и файлы журналов все должны быть понятны для человека. YAML (YAML Не Markup Language) имеет менее подробные данные, чем формат сериализации XML и стал популярным форматом среди разработчиков программного обеспечения главным образом потому, что он легче для человеческого понимания. YAML файлы просто текстовые файлы, содержащие данные, записанные в соответствии с правилом YAML синтаксиса и, как правило, имеет расширение файла.yml. В этой статье вы познакомитесь с основами YAML и как вы можете интегрировать PHP парсер YAML в ваших проектах.

Цели создания

Сообщество разработчиков устало от «зоопарка» различных форматов для конфигов, им хотелось упростить себе жизнь и прийти к единому понятному формату. И в 2001 году Кларк Эванс создал YAML 1.0.

Его основные цели:

  1. быть понятным человеку;
  2. поддерживать структуры данных, родные для языков программирования;
  3. быть переносимым между языками программирования;
  4. использовать цельную модель данных для поддержки обычного инструментария;
  5. поддерживать потоковую обработку;
  6. быть выразительным и расширяемым;
  7. быть лёгким в реализации и использовании.

В официальной документации YAML можно увидеть такое определение:


Рис.1. Определение из официальной документации

Сейчас последняя версия — YAML 1.2, и она в основном используется как формат для файлов конфигурации Ruby on Rails, Dancer, Symfony, GAE framework, Google App Engine и Dart. Также YAML является основным языком описания классов, ресурсов и манифестов для пакетов приложений OpenStack Murano Project и Swagger.io.

YAML vs. JSON

По сути YAML — это расширенная версия известного нам формата JSON.

Чтобы лучше разобраться в формате, давайте сначала рассмотрим пример JSON-конфига:

tsconfig.json

{
  "compilerOptions": {
    "module": "system",
    "noImplicitAny": true,
    "removeComments": true,
    "preserveConstEnums": true,
    "outFile": "../../built/local/tsc.js",
    "sourceMap": false,
    "types": ["node", "lodash", "express"]
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

Этот файл очень легко читается, мы можем быстро определить, что к чему, но … нужно знать, что у JSON-формата есть некоторые ограничения:

  • нельзя создавать переменные;
  • нельзя использовать внешние переменные (например, переменные окружения);
  • нельзя переопределять значения.

Чтобы смягчить эти ограничения, можно заменить переменные с помощью создания JSON-файлов, а также использовать .eslintrc или .eslint.js. Но в других языках программирования это не вариант, поэтому YAML и стал так популярен.

Попробуем переписать наш пример в формате YAML. Пока рассмотрим только синтаксис, а остальные особенности чуть позже.

*Хабр не умеет подсвечивать YAML, так что пришлось разместить картинки

Как вам? Мне на первый взгляд показалось, что очень похоже на Python, но с какими-то опечатками.

Рассмотрим синтаксис подробнее.

Концепции, типы, синтаксис


Отступы

В YAML для разделения информации очень важны отступы. Нужно помнить, что используются только пробелы, табы не допускаются.

При отсутствии отступа перед первым объявлением YAML поймет, что это корень (уровень 0) вашего файла.

Если вы привыкли использовать tab-ы вместо пробелов, то можно использовать какой-нибудь плагин в вашей IDE, чтобы заменить все пропуски на пробелы (например, editorconfig).

Ключ/Значение

Как и в JSON/JS, в YAML есть синтаксис ключ/значение, и вы можете использовать его различными способами:

Комментарии

Чтобы написать комментарий, вы можете использовать #, а затем ваше сообщение.

Это круто, когда нужно задокументировать какое-то решение или сделать заметку в конфиге. К сожалению, мы не можем так сделать в JSON.

Списки

В YAML есть 2 способа написания списков:

  • Синтаксис JSON: массив строк

    Помните, что YAML — это расширенный JSON? Поэтому мы можем использовать его синтаксис

    people: ['Anne', 'John', 'Max']
    

  • Синтаксис дефиса

    Наиболее распространенный и рекомендуемый

    people:
     - Anne
     - John
     - Max
    


Числа

Тут все стандартно: целые числа и числа с плавающей точкой.

Строки

Есть несколько способов объявить строку в YAML:

Если вы хотите использовать какой-нибудь специальный символ, например, _ или @, то нужны будут кавычки.

Напомню, что в JSON у нас есть только один способ написания строк — двойные кавычки.

И главная фишка…

Якорь (переменная или ссылка)

Якорь — это механизм для создания переменных, на которые затем можно ссылаться.

Давайте представим, что вам нужно создать конфигурацию для вашего CI. Он будет иметь версию для production и development сред. Обе версии будут иметь почти одинаковые базовые настройки.

В JSON нам пришлось бы дублировать эти конфиги:

{
  "production": {
    "node_version": "13.0.0",
    "os": "ubuntu",
    "package_manager": "yarn",
    "run": ["yarn install", "NODE_ENV=${ENVIRONMENT} yarn build"],
    "env": {
      "ENVIRONMENT": "production"
    }
  },
  "development": {
    "node_version": "13.0.0",
    "os": "ubuntu",
    "package_manager": "yarn",
    "run": ["yarn install", "NODE_ENV=${ENVIRONMENT} yarn build"],
    "env": {
      "ENVIRONMENT": "development"
    }
  }
}

Копирование и вставка очень раздражают, особенно когда нужно что-то изменить во всех местах.

Якорь решает эту проблему. Для его создания используется символ якоря (&), а для вставки — алиас (*).