Freigeben über


Nullable-Anmerkungen berücksichtigen

Ab .NET 9 bietet JsonSerializer Unterstützung für die eingeschränkte Erzwingung nicht-nullbarer Verweistypen sowohl bei der Serialisierung als auch bei der Deserialisierung. Sie können diese Unterstützung mit dem Flag JsonSerializerOptions.RespectNullableAnnotations umschalten.

So löst der folgende Codeausschnitt beispielsweise während der Serialisierung eine JsonException mit der folgenden Nachricht aus:

Die Eigenschaft oder das Feld „Name“ des Typs „Person“ lässt das Abrufen von NULL-Werten nicht zu. Erwägen Sie die Aktualisierung der Nullbarkeitsanmerkung.

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

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

    record Person(string Name);

Ebenso erzwingt RespectNullableAnnotations Nullwerte bei der Deserialisierung. Der folgende Codeausschnitt löst während der Serialisierung eine JsonException mit der folgenden Nachricht aus:

Der Konstruktorparameter „Name“ für den Typ „Person“ lässt keine NULL-Werte zu. Überlegen Sie, die Nullbarkeitsanmerkung zu aktualisieren.

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

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

    record Person(string Name);

Tipp

Begrenzungen

Aufgrund der Implementierung von Verweistypen, die keine NULL-Werte zulassen, enthält dieses Feature einige wichtige Einschränkungen. Machen Sie sich mit diesen Einschränkungen vertraut, bevor Sie das Feature aktivieren. Die Ursache für das Problem liegt darin, dass der Verweistyp, der keine NULL-Werte zulässt, in der Zwischensprache (Intermediate Language, IL) keine erstklassige Darstellung aufweist. Als solche sind die Ausdrücke MyPoco und MyPoco? aus der Perspektive der Laufzeitreflexion nicht zu unterscheiden. Während der Compiler versucht, dies durch das Ausstellen von Attributmetadaten (siehe sharplab.io-Beispiel) auszugleichen, sind diese Metadaten auf nicht generische Anmerkungen zu Elementen beschränkt, die auf eine bestimmte Typdefinition beschränkt sind. Aufgrund dieser Einschränkung überprüft das Flag nur Nullbarkeitsanmerkungen, die bei nicht generischen Eigenschaften, Feldern und Konstruktorparametern vorhanden sind. System.Text.Json unterstützt keine Durchsetzung der Nullbarkeit für:

  • Typen auf der obersten Ebene, oder den Typ, der beim ersten JsonSerializer.Deserialize() oder JsonSerializer.Serialize() Aufruf übergeben wird.
  • Sammlungselementtypen, z. B. die Typen List<string> und List<string?>, lassen sich nicht voneinander unterscheiden.
  • Alle generischen Eigenschaften, Felder oder Konstruktorparameter.

Wenn Sie in diesen Fällen die Erzwingung von NULL-Zulässigkeit hinzufügen möchten, können Sie Ihren Typ entweder als Struktur modellieren (da sie keine NULL-Werte zulassen), oder einen benutzerdefinierten Konverter erstellen, indem er seine HandleNull-Eigenschaft true überschreibt.

Funktionsschalter

Sie können die Einstellung RespectNullableAnnotations global mithilfe des Funktionsschalters System.Text.Json.Serialization.RespectNullableAnnotationsDefault aktivieren. Fügen Sie der Projektdatei das folgende MSBuild-Element hinzu (z. B. .csproj-Datei):

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

Die RespectNullableAnnotationsDefault-API wurde als Opt-In-Flag in .NET 9 implementiert, um zu vermeiden, dass vorhandene Anwendungen unterbrochen werden. Wenn Sie eine neue Anwendung schreiben, wird dringend empfohlen, dieses Flag in Ihrem Code zu aktivieren.

Beziehung zwischen nullfähigen und optionalen Parametern

RespectNullableAnnotations erweitert die Erzwingung nicht auf nicht angegebene JSON-Werte, da System.Text.Json erforderliche und nicht-nullbare Eigenschaften als orthogonale Konzepte behandelt. So löst der folgende Codeausschnitt während der Deserialisierung beispielsweise keine Ausnahme aus:

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

Dieses Verhalten stammt aus der C#-Sprache selbst, die erforderliche, NULL-Werte zulassende Eigenschaften aufweist:

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

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

Außerdem können Sie optionale Eigenschaften haben, die nicht nullfähig sind.

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

Die gleiche Orthogonalität gilt für Konstruktorparameter:

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

Fehlende Werte im Vergleich zu Nullwerten

Es ist wichtig, den Unterschied zwischen fehlenden JSON-Eigenschaften und Eigenschaften mit expliziten null Werten zu verstehen, wenn Sie festlegen RespectNullableAnnotations. JavaScript unterscheidet zwischen undefined (fehlende Eigenschaft) und null (expliziter Nullwert). .NET hat jedoch kein Konzept für undefined, sodass beide Fälle in .NET zu null deserialisiert werden.

Während der Deserialisierung, wenn RespectNullableAnnotationstrue ist:

  • Ein expliziter NULL-Wert löst eine Ausnahme für nicht nullable Eigenschaften aus. Wirft z. B. eine Ausnahme, wenn {"Name":null} in eine nicht-nullbare string Name-Eigenschaft deserialisiert wird.

  • Eine fehlende Eigenschaft löst keine Ausnahme aus, auch bei nicht nullablen Eigenschaften. Beispielsweise wirft {} keine Ausnahme aus, wenn auf eine nicht-nullfähige string Name Eigenschaft deserialisiert wird. Der Serialisierer legt die Eigenschaft nicht fest und belässt sie beim Standardwert aus dem Konstruktor.The serializer doesn't set the property, leaving it at its default value from the constructor. Bei einem nicht initialisierten nicht nullfähigen Verweistyp führt dies zu nulleiner Compilerwarnung.

    Der folgende Code zeigt, wie eine fehlende Eigenschaft während der Deserialisierung KEINE Ausnahme auslöst:

        public static void RunIt()
        {
    #nullable enable
            JsonSerializerOptions options = new()
            {
                RespectNullableAnnotations = true
            };
    
            // Missing property - does NOT throw an exception.
            string jsonMissing = """{}""";
            var resultMissing = JsonSerializer.Deserialize<Person>(jsonMissing, options);
            Console.WriteLine(resultMissing.Name is null); // True.
        }
    
        record Person(string Name);
    

Dieser Verhaltensunterschied tritt auf, da fehlende Eigenschaften als optional (nicht angegeben) behandelt werden, während explizite null Werte als bereitgestellte Werte behandelt werden, die gegen die nicht nullable Einschränkung verstoßen. Wenn Sie erzwingen müssen, dass eine Eigenschaft im JSON vorhanden sein muss, verwenden Sie den required Modifizierer, oder konfigurieren Sie die Eigenschaft nach Bedarf mithilfe JsonRequiredAttribute des Vertragsmodells.

Siehe auch