Partilhar via


Respeitar anotações anuláveis

A partir do .NET 9, JsonSerializer tem suporte (limitado) para imposição de tipo de referência não anulável na serialização e desserialização. Você pode alternar esse suporte usando o JsonSerializerOptions.RespectNullableAnnotations sinalizador.

Por exemplo, o trecho de código a seguir lança um JsonException durante a serialização com uma mensagem como:

A propriedade ou campo 'Nome' no tipo 'Pessoa' não permite obter valores nulos. Considere atualizar sua anotação de anulabilidade.

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

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

    record Person(string Name);

Da mesma forma, RespectNullableAnnotations impõe a anulabilidade na desserialização. O trecho de código a seguir lança um JsonException durante a serialização com uma mensagem como:

O parâmetro do construtor 'Name' no tipo 'Person' não permite valores nulos. Considere atualizar sua anotação de anulabilidade.

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

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

    record Person(string Name);

Gorjeta

Limitações

Devido à forma como os tipos de referência não anuláveis são implementados, este recurso vem com algumas limitações importantes. Familiarize-se com essas limitações antes de ativar o recurso. A raiz do problema é que a anulabilidade do tipo de referência não tem representação de primeira classe em linguagem intermediária (IL). Como tal, as expressões MyPoco e MyPoco? são indistinguíveis da perspetiva da reflexão em tempo de execução. Enquanto o compilador tenta compensar isso emitindo metadados de atributo (consulte sharplab.io exemplo), esses metadados são restritos a anotações de membros não genéricos que têm escopo para uma definição de tipo específica. Essa limitação é a razão pela qual o sinalizador só valida anotações de anulabilidade que estão presentes em propriedades não genéricas, campos e parâmetros do construtor. System.Text.Json não suporta a aplicação de anulabilidade em:

  • Tipos de nível superior ou o tipo que é passado ao fazer a primeira JsonSerializer.Deserialize() ou JsonSerializer.Serialize() chamada.
  • Tipos de elementos de coleção — por exemplo, os List<string> tipos e List<string?> são indistinguíveis.
  • Quaisquer propriedades, campos ou parâmetros do construtor que sejam genéricos.

Se você quiser adicionar imposição de anulabilidade nesses casos, modele seu tipo para ser um struct (já que eles não admitem valores nulos) ou crie um conversor personalizado que substitua sua HandleNull propriedade para true.

Mudança de funcionalidade

Você pode ativar a RespectNullableAnnotations configuração globalmente usando a System.Text.Json.Serialization.RespectNullableAnnotationsDefault opção de recurso. Adicione o seguinte item MSBuild ao seu arquivo de projeto (por exemplo, arquivo .csproj ):

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

A RespectNullableAnnotationsDefault API foi implementada como um sinalizador de aceitação no .NET 9 para evitar a interrupção de aplicativos existentes. Se você estiver escrevendo um novo aplicativo, é altamente recomendável que você habilite esse sinalizador em seu código.

Relação entre parâmetros anuláveis e opcionais

RespectNullableAnnotations não estende a imposição a valores JSON não especificados, porque System.Text.Json trata as propriedades necessárias e não anuláveis como conceitos ortogonais. Por exemplo, o trecho de código a seguir não gera uma exceção durante a desserialização:

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; }
}

Esse comportamento deriva da própria linguagem C#, onde você pode ter propriedades necessárias que são anuláveis:

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

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

E você também pode ter propriedades opcionais que não são anuláveis:

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

A mesma ortogonalidade aplica-se aos parâmetros do construtor:

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

Consulte também