Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 vs. null values

It's important to understand the distinction between missing JSON properties and properties with explicit `null` values when using <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.

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 <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,35 @@ 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.

// Explicit null value - DOES throw an exception.
string jsonNull = """{"Name":null}""";
try
{
JsonSerializer.Deserialize<Person>(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);
// </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();