✨ A lightweight library for handling partial updates in ASP.NET Core APIs without JSON Patch.
When building APIs for partial updates (PATCH requests), developers often face challenges such as:
• 🧩 Differentiating between an explicit null and an omitted field
• 🛡️ Avoiding overwriting fields that weren't sent by the client
• 📚 Ensuring accurate schema documentation with Swagger/Swashbuckle
JsonPatchBind elegantly solves these problems using the Optional<T> type!
• Field Presence Tracking - Know if a property was included (even if null)
• Safe Updates - Only modify fields provided in the request
• Native JSON Support - Works seamlessly with System.Text.Json
• Swagger Ready - Automatic schema generation for Optional<T>
| Feature | Description |
|---|---|
Optional<T> Wrapper |
HasValue = true → Field was sent (value can be null) |
| Custom JSON Converter | Handles serialization/deserialization perfectly |
| Swagger Schema Filter | Shows Optional<T> as nullable fields in docs |
| ASP.NET Core Integration | Works with [FromBody] out of the box |
| Zero Dependencies | Pure .NET solution |
While JSON Patch (RFC 6902) is standard, it has drawbacks:
| Issue | JsonPatchBind | JSON Patch |
|---|---|---|
| Payload Complexity | Simple JSON | Complex operations |
| Null Handling | Clear differentiation | Ambiguous |
| Swagger Support | Automatic | Manual config needed |
| Validation | Minimal | Extensive |
| Criteria | JsonPatchBind ✅ | JSON Patch ❌ |
|---|---|---|
| Simple updates | Perfect fit | Overkill |
| Client simplicity | Easy JSON | Complex ops |
| Field tracking | Built-in | Not native |
| Swagger integration | Auto-magic | Manual work |
- Add the NuGet package:
dotnet add package JsonPatchBind- Configure Services:
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new OptionalConverterFactory());
});- Add Swagger Support:
builder.Services.AddSwaggerGen(c =>
{
c.SchemaFilter<OptionalSchemaFilter>();
});Model definition:
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
[ValidateOptional(typeof(StringLengthAttribute), 32)]
[ValidateOptional(typeof(RegularExpressionAttribute), "\\d+")]
public Optional<string> SurName { get; set; }
public Optional<int> Age { get; set; }
public Optional<AddressDto> Address { get; set; }
}Controller:
[HttpPatch("{id}")]
public IActionResult UpdateUser(int id, [FromBody] UserDto user)
{
// Only update fields that were actually sent
if (user.SurName.HasValue)
{
// Handle surname update
}
}