From aba56422a75e6b13e35066433c82d9b1997dea9e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:10:15 +0000 Subject: [PATCH 1/6] Initial plan From 90fa7eab281241c2c09ecf1cdc2cc18a1cb23f17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:15:30 +0000 Subject: [PATCH 2/6] Add documentation explaining missing vs null values in nullable annotations Co-authored-by: gewarren <24882762+gewarren@users.noreply.github.com> --- .../system-text-json/nullable-annotations.md | 15 +++++++++ .../snippets/nullable-annotations/Nullable.cs | 32 +++++++++++++++++++ .../snippets/nullable-annotations/Program.cs | 3 +- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/docs/standard/serialization/system-text-json/nullable-annotations.md b/docs/standard/serialization/system-text-json/nullable-annotations.md index 8a5f7d65f44ad..15578af490829 100644 --- a/docs/standard/serialization/system-text-json/nullable-annotations.md +++ b/docs/standard/serialization/system-text-json/nullable-annotations.md @@ -84,6 +84,21 @@ record MyPoco( ); ``` +## Missing values vs. null values + +It's important to understand the distinction between missing JSON properties and properties with explicit `null` values when using . JavaScript distinguishes between `undefined` (missing property) and `null` (explicit null value). However, .NET doesn't have an `undefined` concept, so both cases deserialize to `null` in .NET. + +When `RespectNullableAnnotations` is `true`: + +- **Explicit null value**: Throws an exception for non-nullable properties. For example, `{"Name":null}` throws an exception when deserializing to a non-nullable `string Name` property. +- **Missing property**: Does NOT throw an exception, even for non-nullable properties. For example, `{}` does not throw an exception when deserializing to a non-nullable `string Name` property. The property is set to `null`. + +The following code demonstrates this difference: + +:::code language="csharp" source="snippets/nullable-annotations/Nullable.cs" id="MissingVsNull"::: + +This behavior occurs because missing properties are treated as optional (not provided), while explicit `null` values are treated as provided values that violate the non-nullable constraint. If you need to enforce that a property must be present in the JSON, use the `required` modifier or configure the property as required using or the contracts model. + ## See also - [Non-optional constructor parameters](required-properties.md#non-optional-constructor-parameters) diff --git a/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Nullable.cs b/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Nullable.cs index 99cbf61b9a71f..2fd21a6cc6312 100644 --- a/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Nullable.cs +++ b/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Nullable.cs @@ -58,3 +58,35 @@ record Person(string Name); // } +public static class Nullable4 +{ + // + public static void RunIt() + { +#nullable enable + JsonSerializerOptions options = new() + { + RespectNullableAnnotations = true + }; + + // Missing property - does NOT throw an exception. + string jsonMissing = """{}"""; + var resultMissing = JsonSerializer.Deserialize(jsonMissing, options); + Console.WriteLine(resultMissing.Name is null); // True. + + // Explicit null value - DOES throw an exception. + string jsonNull = """{"Name":null}"""; + try + { + JsonSerializer.Deserialize(jsonNull, options); + } + catch (JsonException ex) + { + Console.WriteLine(ex.Message); // The constructor parameter 'Name' on type 'Person' doesn't allow null values. + } + } + + record Person(string Name); + // +} + diff --git a/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Program.cs b/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Program.cs index 816681470e389..82413ada4b464 100644 --- a/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Program.cs +++ b/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Program.cs @@ -1,3 +1,4 @@ //Nullable1.RunIt(); //Nullable2.RunIt(); -Nullable3.RunIt(); +//Nullable3.RunIt(); +Nullable4.RunIt(); From b52b531dabeb2975dc75f457681da1256e438503 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:17:33 +0000 Subject: [PATCH 3/6] Update frontmatter with ai-usage and current date Co-authored-by: gewarren <24882762+gewarren@users.noreply.github.com> --- .../serialization/system-text-json/nullable-annotations.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/standard/serialization/system-text-json/nullable-annotations.md b/docs/standard/serialization/system-text-json/nullable-annotations.md index 15578af490829..8c11b3667f32d 100644 --- a/docs/standard/serialization/system-text-json/nullable-annotations.md +++ b/docs/standard/serialization/system-text-json/nullable-annotations.md @@ -1,8 +1,9 @@ --- title: Respect nullable annotations description: "Learn how to configure serialization and deserialization to respect nullable annotations." -ms.date: 10/22/2024 +ms.date: 10/20/2025 no-loc: [System.Text.Json, Newtonsoft.Json] +ai-usage: ai-assisted --- # Respect nullable annotations From 99a193d1abe8289845d32430b87d7e4fe9591b4d Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:28:11 -0700 Subject: [PATCH 4/6] Correct formatting and wording in nullable annotations doc --- .../system-text-json/nullable-annotations.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/standard/serialization/system-text-json/nullable-annotations.md b/docs/standard/serialization/system-text-json/nullable-annotations.md index 8c11b3667f32d..bdaec4d3119d6 100644 --- a/docs/standard/serialization/system-text-json/nullable-annotations.md +++ b/docs/standard/serialization/system-text-json/nullable-annotations.md @@ -85,20 +85,20 @@ record MyPoco( ); ``` -## Missing values vs. null values +## Missing values versus null values -It's important to understand the distinction between missing JSON properties and properties with explicit `null` values when using . JavaScript distinguishes between `undefined` (missing property) and `null` (explicit null value). However, .NET doesn't have an `undefined` concept, so both cases deserialize to `null` in .NET. +It's important to understand the distinction between *missing JSON properties* and *properties with explicit `null` values* when you set . JavaScript distinguishes between `undefined` (missing property) and `null` (explicit null value). However, .NET doesn't have an `undefined` concept, so both cases deserialize to `null` in .NET. -When `RespectNullableAnnotations` is `true`: +During deserialization, when `RespectNullableAnnotations` is `true`: -- **Explicit null value**: Throws an exception for non-nullable properties. For example, `{"Name":null}` throws an exception when deserializing to a non-nullable `string Name` property. -- **Missing property**: Does NOT throw an exception, even for non-nullable properties. For example, `{}` does not throw an exception when deserializing to a non-nullable `string Name` property. The property is set to `null`. +- An **explicit null value** throws an exception for non-nullable properties. For example, `{"Name":null}` throws an exception when deserializing to a non-nullable `string Name` property. +- A **missing property** doesn't throw an exception, even for non-nullable properties. For example, `{}` doesn't throw an exception when deserializing to a non-nullable `string Name` property. The property is set to `null`. -The following code demonstrates this difference: + The following code shows how a missing property does NOT throw an exception during deserialization: -:::code language="csharp" source="snippets/nullable-annotations/Nullable.cs" id="MissingVsNull"::: + :::code language="csharp" source="snippets/nullable-annotations/Nullable.cs" id="MissingVsNull"::: -This behavior occurs because missing properties are treated as optional (not provided), while explicit `null` values are treated as provided values that violate the non-nullable constraint. If you need to enforce that a property must be present in the JSON, use the `required` modifier or configure the property as required using or the contracts model. +This behavior difference occurs because missing properties are treated as optional (not provided), while explicit `null` values are treated as provided values that violate the non-nullable constraint. If you need to enforce that a property must be present in the JSON, use the `required` modifier or configure the property as required using or the contracts model. ## See also From e296c5aec963e0942ab6ea8ec179e76bc550dc54 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:32:23 -0700 Subject: [PATCH 5/6] Remove null value exception handling example Removed example demonstrating exception on explicit null value during deserialization. --- .../snippets/nullable-annotations/Nullable.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Nullable.cs b/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Nullable.cs index 2fd21a6cc6312..86c2fc086f4de 100644 --- a/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Nullable.cs +++ b/docs/standard/serialization/system-text-json/snippets/nullable-annotations/Nullable.cs @@ -73,17 +73,6 @@ public static void RunIt() string jsonMissing = """{}"""; var resultMissing = JsonSerializer.Deserialize(jsonMissing, options); Console.WriteLine(resultMissing.Name is null); // True. - - // Explicit null value - DOES throw an exception. - string jsonNull = """{"Name":null}"""; - try - { - JsonSerializer.Deserialize(jsonNull, options); - } - catch (JsonException ex) - { - Console.WriteLine(ex.Message); // The constructor parameter 'Name' on type 'Person' doesn't allow null values. - } } record Person(string Name); From c4b519c34dc131e3ac049246a520b38bdf848c39 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 06:14:39 +0000 Subject: [PATCH 6/6] Clarify that missing properties are left at default values, not set to null Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- .../serialization/system-text-json/nullable-annotations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/standard/serialization/system-text-json/nullable-annotations.md b/docs/standard/serialization/system-text-json/nullable-annotations.md index bdaec4d3119d6..06193824a24c8 100644 --- a/docs/standard/serialization/system-text-json/nullable-annotations.md +++ b/docs/standard/serialization/system-text-json/nullable-annotations.md @@ -92,7 +92,7 @@ It's important to understand the distinction between *missing JSON properties* a During deserialization, when `RespectNullableAnnotations` is `true`: - An **explicit null value** throws an exception for non-nullable properties. For example, `{"Name":null}` throws an exception when deserializing to a non-nullable `string Name` property. -- A **missing property** doesn't throw an exception, even for non-nullable properties. For example, `{}` doesn't throw an exception when deserializing to a non-nullable `string Name` property. The property is set to `null`. +- A **missing property** doesn't throw an exception, even for non-nullable properties. For example, `{}` doesn't throw an exception when deserializing to a non-nullable `string Name` property. The serializer doesn't set the property, leaving it at its default value from the constructor. For an uninitialized non-nullable reference type, this results in `null`, which triggers a compiler warning. The following code shows how a missing property does NOT throw an exception during deserialization: