Condividi tramite


Rispettare le annotazioni nullable

A partire da .NET 9, JsonSerializer include (limitato) supporto per l'imposizione dei tipi di riferimento non nullable sia nella serializzazione che nella deserializzazione. È possibile attivare o disattivare questo supporto usando il JsonSerializerOptions.RespectNullableAnnotations flag .

Ad esempio, il frammento di codice seguente genera un'eccezione JsonException durante la serializzazione con un messaggio simile al seguente:

La proprietà o il campo 'Name' nel tipo 'Person' non consente di ottenere valori Null. Prendere in considerazione l'aggiornamento dell'annotazione relativa ai valori 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);

Analogamente, RespectNullableAnnotations applica nullbility alla deserializzazione. Il frammento di codice seguente genera un'eccezione JsonException durante la serializzazione con un messaggio simile al seguente:

Il parametro del costruttore 'Name' nel tipo 'Person' non consente valori Null. Prendere in considerazione l'aggiornamento dell'annotazione relativa ai valori 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);

Suggerimento

Limiti

A causa della modalità di implementazione dei tipi riferimento non nullable, questa funzionalità presenta alcune limitazioni importanti. Acquisire familiarità con queste limitazioni prima di attivare la funzionalità. La radice del problema è che il supporto del tipo di riferimento non ha alcuna rappresentazione di prima classe nel linguaggio intermedio (IL). Di conseguenza, le espressioni MyPoco e MyPoco? sono indistinguabili dal punto di vista della reflection in fase di esecuzione. Mentre il compilatore tenta di crearlo creando metadati dell'attributo (vedere sharplab.io esempio), questi metadati sono limitati a annotazioni membro non generiche con ambito a una definizione di tipo particolare. Questa limitazione è il motivo per cui il flag convalida solo le annotazioni di valori Null che sono presenti su proprietà, campi e parametri del costruttore non generici. System.Text.Json non supporta l'applicazione dei valori Null per:

  • Tipi di primo livello o il tipo passato quando si effettua la prima JsonSerializer.Deserialize() chiamata o JsonSerializer.Serialize() .
  • Tipi di elemento raccolta, ad esempio i List<string> tipi e List<string?> sono indistinguishable.
  • Qualsiasi proprietà, campi o parametri del costruttore generici.

Se si vuole aggiungere l'imposizione dei valori Null in questi casi, modellare il tipo in modo che sia uno struct (poiché non ammettono valori Null) o creare un convertitore personalizzato che esegue l'override della relativa HandleNull proprietà su true.

Cambio di funzionalità

È possibile attivare l'impostazione a livello globale usando l'opzione RespectNullableAnnotations di System.Text.Json.Serialization.RespectNullableAnnotationsDefault funzionalità. Aggiungere l'elemento MSBuild seguente al file di progetto (ad esempio, file con estensione csproj ):

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

L'API RespectNullableAnnotationsDefault è stata implementata come flag di consenso esplicito in .NET 9 per evitare l'interruzione delle applicazioni esistenti. Se si sta scrivendo una nuova applicazione, è consigliabile abilitare questo flag nel codice.

Relazione tra parametri nullable e facoltativi

RespectNullableAnnotations non estende l'imposizione ai valori JSON non specificati, perché System.Text.Json considera le proprietà obbligatorie e non nullable come concetti ortogonali. Ad esempio, il frammento di codice seguente non genera un'eccezione durante la deserializzazione:

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

Questo comportamento deriva dal linguaggio C# stesso, in cui è possibile avere proprietà obbligatorie che sono nullable:

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

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

Inoltre, è possibile avere proprietà facoltative che non sono nullable:

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

La stessa ortogonalità si applica ai parametri del costruttore:

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

Vedi anche