Repository navigation
Description
The Problem
We've been building HTTP SDKs and OpenAPI clients in C#, and we've been running into this issue with representing three distinct states for a field:
- Undefined: Field not provided (e.g., PATCH request - don't update this field)
- Null: Field explicitly set to null (e.g., PATCH request - clear this field)
- Value: Field set to a specific value (e.g., PATCH request - update to this value)
The problem is that C#'s nullable types (string?, int?) can only represent two states: null or value. There's no way to distinguish between "not provided" and "explicitly set to null."
Example
Let's say you have a PATCH endpoint for updating a user profile:
PATCH /users/123
{
"name": "Alice", // Update name to "Alice"
"email": null, // Clear email (set to null)
// age not present // Don't touch age (leave unchanged)
}With standard C# types, you can't express this:
string? Email = null- Does this mean "clear it" or "not provided"?- There's no way to tell if the user omitted a field vs explicitly sent null
What I've Built So Far
I ended up implementing an Optional<T> type that wraps nullable types to give us that third state:
public readonly struct Optional<T> : IOptional, IEquatable<Optional<T>>
{
public static Optional<T> Undefined { get; }
public static Optional<T> Of(T value) { }
public bool IsDefined { get; }
public bool IsUndefined { get; }
public T Value { get; }
}How It Works
Here's what using it looks like:
public class UpdateUserRequest
{
// null => null, or value => value
[Nullable]
public string? Description { get; set; }
// Optional, nullable - can be undefined => undefined, null => null, or value => null
[Optional, Nullable]
public Optional<string?> Email { get; set; } = Optional<string?>.Undefined;
// null => undefined, value => value
[Optional]
public string? Name { get; set; }
}
// Usage
var request = new UpdateUserRequest
{
Name = "Alice", // Update name
Email = Optional<string?>.Of(null), // Clear email (explicit null)
// Description omitted // Don't touch description
};Serialization Behavior
Quick reference for how different combinations serialize:
| Property Type | Value | JSON Behavior |
|---|---|---|
string? Field |
null |
Omitted (JsonIgnoreCondition.WhenWritingNull) |
[Nullable] string? Field |
null |
Included as null |
[Nullable] string? Field |
Of("text") |
Included as "text" |
[Optional] string? Field |
null |
Omitted |
[Optional] string? Field |
Of("text") |
Included as "text" |
[Optional] Optional<string> Field |
Undefined |
Omitted |
[Optional] Optional<string> Field |
Of("text") |
Included as "text" |
[Optional, Nullable] Optional<string?> Field |
Undefined |
Omitted |
[Optional, Nullable] Optional<string?> Field |
Of(null) |
Included as null |
[Optional, Nullable] Optional<string?> Field |
Of("text") |
Included as "text" |
What's in the Repo
The implementation includes:
- Core Type:
Optional<T>struct with IEquatable support - JSON Serialization: Custom
JsonConverterandJsonTypeInfoResolverusing reflection to check[Optional]and[Nullable]attributes - Testing Support: NUnit extensions for comparing
Optional<T>values in assertions - Complete Test Suite: 20+ tests covering all serialization/deserialization scenarios
Key files in this repository:
Optional.cs- Core implementation with IOptional interfaceOpenApiSerialization.cs- JSON serialization with attribute-based modifiersOptionalAttribute.cs&NullableAttribute.cs- Attribute markersOpenApiSerializationTests.cs- Comprehensive test coverageOptionalComparerExtensions.cs- NUnit test helpers
Questions
I'm looking for guidance on whether this is the right approach:
1. Is there an existing .NET pattern I should be using instead?
- Are there plans to add first-class support for "undefined vs null" in future C# versions?
- Should I be using a different approach with System.Text.Json?
- Are there existing BCL types or patterns that address this?
2. JSON serialization - am I doing this right?
- Is using
JsonTypeInfoResolverwith reflection on custom attributes reasonable? - Should I be leveraging different System.Text.Json features?
- Any performance implications I should be aware of?
3. API design - the Optional<T?> question
I chose to separate concerns:
Optional<T>= "Is the field present in the request?"T?= "Can the field value be null?"
This leads to Optional<T?> for optional nullable fields. Is this the right approach, or should I build null handling into Optional<T> itself (like having an Optional<T>.Null state)?
Full working implementation with tests is in this repo. All tests pass on .NET 8.0, .NET 10.0, and .NET Framework 4.6.2.
Would really appreciate any guidance on the recommended .NET approach here!
Activity
MihuBot commentedon Jan 14, 2026
I'm a bot. Here are possible related and/or duplicate issues (I may be wrong):
dotnet-policy-service commentedon Jan 15, 2026
Tagging subscribers to this area: @dotnet/area-system-text-json
See info in area-owners.md if you want to be subscribed.
Swimburger commentedon Jan 15, 2026
I didn't notice I was posting under the Fern support account. I'm the author of this issue.