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


Примечания, допускающие значение NULL

Начиная с .NET 9, JsonSerializer поддерживает (ограниченную) поддержку ненулевого ссылочного типа в сериализации и десериализации. Эту поддержку можно переключить с помощью флага JsonSerializerOptions.RespectNullableAnnotations .

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

Свойство или поле "Имя" в типе "Person" не позволяет получать значения NULL. Рассмотрите возможность обновления заметки о допустимости null.

    public static void RunIt()
    {
#nullable enable
        JsonSerializerOptions options = new()
        {
            RespectNullableAnnotations = true
        };

        Person invalidValue = new(Name: null!);
        JsonSerializer.Serialize(invalidValue, options);
    }

    record Person(string Name);

Аналогичным образом RespectNullableAnnotations применяется возможность null при десериализации. Следующий фрагмент кода создает JsonException исключение во время сериализации с сообщением, например:

Параметр конструктора "Имя" типа "Person" не разрешает значения NULL. Рассмотрите возможность обновления заметки о допустимости null.

    public static void RunIt()
    {
#nullable enable
        JsonSerializerOptions options = new()
        {
            RespectNullableAnnotations = true
        };

        string json = """{"Name":null}""";
        JsonSerializer.Deserialize<Person>(json, options);
    }

    record Person(string Name);

Совет

  • Можно настроить значение NULL на уровне отдельного свойства с помощью IsGetNullable свойств и IsSetNullable свойств.
  • Компилятор C# использует [NotNull]атрибуты , [AllowNull][MaybeNull]а также [DisallowNull] атрибуты для точной настройки заметок в методах получения и заданиях. Эти атрибуты также распознаются этой System.Text.Json функцией. (Дополнительные сведения об атрибутах см. в разделе Атрибуты для статического анализа состояния NULL.)

Ограничения

Благодаря реализации ссылочных типов, не допускающих значение NULL, эта функция имеет некоторые важные ограничения. Ознакомьтесь с этими ограничениями перед включением функции. Корень проблемы заключается в том, что ссылочный тип NULL не имеет представления первого класса на промежуточном языке (IL). Таким образом, выражения и MyPoco? неразличимы MyPoco с точки зрения отражения времени выполнения. Хотя компилятор пытается сделать это путем создания метаданных атрибутов (см . sharplab.io примере), эти метаданные ограничены заметками не универсальных элементов, которые относятся к определенному определению типа. Это ограничение является причиной того, что флаг проверяет только заметки nullability, которые присутствуют в не универсальных свойствах, полях и параметрах конструктора. System.Text.Json не поддерживает принудительное применение null в:

  • Типы верхнего уровня или тип, передаваемый при выполнении первого JsonSerializer.Deserialize() или JsonSerializer.Serialize() вызова.
  • Типы элементов коллекции, например, List<string> и List<string?> типы являются неотличимыми.
  • Все свойства, поля или параметры конструктора, которые являются универсальными.

Если вы хотите добавить принудительное применение null в этих случаях, либо моделируйте тип структуры (так как они не допускают значения NULL), либо создайте настраиваемый преобразователь, который переопределяет его HandleNull свойство true.

Переключатели функции

Вы можете включить RespectNullableAnnotations параметр глобально с помощью System.Text.Json.Serialization.RespectNullableAnnotationsDefault переключателя функций. Добавьте следующий элемент MSBuild в файл проекта (например, CSPROJ-файл ):

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Text.Json.Serialization.RespectNullableAnnotationsDefault" Value="true" />
</ItemGroup>

RespectNullableAnnotationsDefault API был реализован как флаг согласия в .NET 9, чтобы избежать нарушения существующих приложений. Если вы пишете новое приложение, настоятельно рекомендуется включить этот флаг в коде.

Связь между допустимыми значениями NULL и необязательными параметрами

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

public static void RunIt()
{
    JsonSerializerOptions options = new()
    {
        RespectNullableAnnotations = true
    };
    var result = JsonSerializer.Deserialize<MyPoco>("{}", options);
    Console.WriteLine(result.Name is null); // True.
}

class MyPoco
{
    public string Name { get; set; }
}

Это поведение связано с самим языком C#, где можно иметь необходимые свойства, допускающие значение NULL:

MyPoco poco = new() { Value = null }; // No compiler warnings.

class MyPoco
{
    public required string? Value { get; set; }
}

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

class MyPoco
{
    public string Value { get; set; } = "default";
}

Та же ортогональность применяется к параметрам конструктора:

record MyPoco(
    string RequiredNonNullable,
    string? RequiredNullable,
    string OptionalNonNullable = "default",
    string? OptionalNullable = "default"
    );

См. также