Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -84,6 +85,21 @@ record MyPoco(
);
```

## Missing values versus null values

It's important to understand the distinction between *missing JSON properties* and *properties with explicit `null` values* when you set <xref:System.Text.Json.JsonSerializerOptions.RespectNullableAnnotations>. 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.
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cross-reference tag should use the property name format RespectNullableAnnotations instead of the full type path. Change <xref:System.Text.Json.JsonSerializerOptions.RespectNullableAnnotations> to just reference the property consistently with how it's used elsewhere in the document (as RespectNullableAnnotations).

Suggested change
It's important to understand the distinction between *missing JSON properties* and *properties with explicit `null` values* when you set <xref:System.Text.Json.JsonSerializerOptions.RespectNullableAnnotations>. 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 <xref:RespectNullableAnnotations>. 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.

Copilot uses AI. Check for mistakes.


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 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:

:::code language="csharp" source="snippets/nullable-annotations/Nullable.cs" id="MissingVsNull":::

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 <xref:System.Text.Json.Serialization.JsonRequiredAttribute> or the contracts model.

## See also

- [Non-optional constructor parameters](required-properties.md#non-optional-constructor-parameters)
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,24 @@ record Person(string Name);
// </Deserialization>
}

public static class Nullable4
{
// <MissingVsNull>
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);
// </MissingVsNull>
}

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//Nullable1.RunIt();
//Nullable2.RunIt();
Nullable3.RunIt();
//Nullable3.RunIt();
Nullable4.RunIt();