Uwzględnianie adnotacji dopuszczanych do wartości null
Począwszy od platformy .NET 9, JsonSerializer ma (ograniczoną) obsługę wymuszania typu referencyjnego bez wartości null zarówno w serializacji, jak i deserializacji. Tę obsługę można przełączać przy użyciu flagi JsonSerializerOptions.RespectNullableAnnotations .
Na przykład poniższy fragment kodu zgłasza JsonException podczas serializacji komunikat podobny do następującego:
Właściwość lub pole "Name" w typie "Person" nie zezwala na pobieranie wartości null. Rozważ zaktualizowanie adnotacji o wartości 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 Podobnie wymusza wartość null w przypadku deserializacji. Poniższy fragment kodu zgłasza JsonException podczas serializacji komunikat podobny do następującego:
Parametr konstruktora "Name" w typie "Person" nie zezwala na wartości null. Rozważ zaktualizowanie adnotacji o wartości 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);
Napiwek
- Wartość null można skonfigurować na poziomie poszczególnych właściwości przy użyciu IsGetNullable właściwości i IsSetNullable .
- Kompilator języka C# używa
[NotNull]
atrybutów ,[AllowNull]
,[MaybeNull]
i[DisallowNull]
do dostosowywania adnotacji w elementach getters i setters. Te atrybuty są również rozpoznawane przez tę System.Text.Json funkcję. (Aby uzyskać więcej informacji na temat atrybutów, zobacz Atrybuty analizy statycznej stanu null).
Ograniczenia
Ze względu na sposób implementacji typów odwołań bez wartości null ta funkcja zawiera pewne ważne ograniczenia. Zapoznaj się z tymi ograniczeniami przed włączeniem funkcji. Głównym elementem problemu jest to, że typ odwołania null nie ma pierwszej klasy reprezentacji w języku pośrednim (IL). W związku z tym wyrażenia MyPoco
i MyPoco?
są nie do odróżnienia z perspektywy odbicia w czasie wykonywania. Podczas gdy kompilator próbuje nadrobić to, emitując metadane atrybutów (zobacz sharplab.io przykład), te metadane są ograniczone do niegenerycznych adnotacji składowych, które są ograniczone do określonej definicji typu. To ograniczenie jest powodem, dla którego flaga weryfikuje tylko adnotacje dopuszczające wartość null, które znajdują się we właściwościach niegenerycznych, polach i parametrach konstruktora. System.Text.Json nie obsługuje wymuszania wartości null w:
- Typy najwyższego poziomu lub typ przekazywany podczas pierwszego
JsonSerializer.Deserialize()
wywołania lubJsonSerializer.Serialize()
. - Typy elementów kolekcji — na przykład
List<string>
typy iList<string?>
są nie do odróżnienia. - Wszelkie właściwości, pola lub parametry konstruktora, które są ogólne.
Jeśli chcesz dodać wymuszanie wartości null w tych przypadkach, możesz modelować typ jako strukturę (ponieważ nie przyznają wartości null) lub utworzyć niestandardowy konwerter, który zastępuje jego HandleNull właściwość .true
Przełączniki funkcji
Ustawienie można włączyć RespectNullableAnnotations
globalnie przy użyciu przełącznika System.Text.Json.Serialization.RespectNullableAnnotationsDefault
funkcji. Dodaj następujący element MSBuild do pliku projektu (na przykład plik csproj ):
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Text.Json.Serialization.RespectNullableAnnotationsDefault" Value="true" />
</ItemGroup>
Interfejs API został zaimplementowany jako flaga RespectNullableAnnotationsDefault
zgody na platformie .NET 9, aby uniknąć przerywania istniejących aplikacji. Jeśli piszesz nową aplikację, zdecydowanie zaleca się włączenie tej flagi w kodzie.
Relacja między parametrami dopuszczanymi do wartości null i opcjonalnymi
RespectNullableAnnotations nie rozszerza wymuszania na nieokreślone wartości JSON, ponieważ System.Text.Json traktuje wymagane i niepuste właściwości jako pojęcia ortogonalne. Na przykład poniższy fragment kodu nie zgłasza wyjątku podczas deserializacji:
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; }
}
To zachowanie wynika z samego języka C#, w którym można mieć wymagane właściwości, które są dopuszczające wartość null:
MyPoco poco = new() { Value = null }; // No compiler warnings.
class MyPoco
{
public required string? Value { get; set; }
}
Możesz również mieć opcjonalne właściwości, które nie są dopuszczające wartości null:
class MyPoco
{
public string Value { get; set; } = "default";
}
Ta sama ortogonalność ma zastosowanie do parametrów konstruktora:
record MyPoco(
string RequiredNonNullable,
string? RequiredNullable,
string OptionalNonNullable = "default",
string? OptionalNullable = "default"
);