JsonPatch
Overview
JGoedde.JsonPatch is a library designed to facilitate the application of JSON Patch operations on objects in C#. This package enables you to apply operations (such as add, remove, replace, etc.) to a specific object of type T using a set of provided patch operations.
It wraps and extends the functionality of ASP.NET Core´s JsonPatchDocument<T> by providing a more flexible, easy-to-use interface to create patch operations from JSON-like input, making it easier to apply partial updates to objects in a strongly-typed manner.
Key Features
- JSON Patch Support: Supports common JSON Patch operations (add, remove, replace) on objects.
- Strongly Typed: Works with any class (
T : class) and ensures that patch operations are applied to the correct properties. - Path Navigation: Allows you to navigate properties or collections (arrays) through a path expression (e.g.,
/property/subpropertyor/array/0/subproperty). - Dynamic Type Handling: Can handle nested properties and collections, ensuring correct type resolution even in complex object graphs.
- PatchObservable: Can observe changes on (readable) properties of an object
Installation
To install the package, use NuGet:
Install-Package JGoedde.JsonPatchOr add it to your .csproj file:
<PackageReference Include="JGoedde.JsonPatch" Version="1.0.0" />Usage
[HttpPatch("{id}")]
public async Task<IActionResult> Patch(string id,[FromBody] JsonPatch<MyObject> model)
{
JsonPatchDocument<MyObject> patchDocument = model.CreatePatchDocument();
// Use patchDocument...
}Class and Method Breakdown
PatchObservable<T>
Observe changes on objects and trigger listeners with JsonPatch compliant patches
Example
// class needs to inherit from PatchObservable<self>
public class TestClassPerson:PatchObservable<TestClassPerson>
{
public string Id{get;set;} = Guid.NewGuid().ToString();
public string? Name { get; set; }
public int Age { get; set; }
public DateTime Birth { get; set; }
public List<string>? Titles { get; set; }
// Deep structures are possible - self-referencing is not supported, will not be detected and will result in stack-overflows
public List<TestClassPerson>? Children { get; set; }
}
// Usage:
var person = new TestClassPerson();
// Add a listener - you can add multiple
person.Listen(async (personId,patches) => {
// Use personId and patches
// return false to remove/stop listener
return true;
}, person.Id);
// In a transaction all changes on public properties will be observed - after the using all changes are combined to a patch and handled by the listeners
await using(person.Transaction()){
person.Name = "Test";
person.Age = 20;
person.Birth = DateTime.Now;
if(person.Titles == null)
person.Titles = new List<string>();
person.Titles.Add("Test");
}
// Alternatively you can change single properties with `Change`
await person.Change(p=>p.Name, "New Name");
// Change can also be called with an action
await person.Change(p=>{
p.Name = "New Name";
p.Age = 30;
});
// After awaiting the method `Change` or the transaction all listeners are handled
// Listeners are running in parallelJsonPatch<T>
This is the main class used to handle the patch operations.
Properties:
- Operations: A list of
Operationobjects that define the patch operations (add, remove, replace, etc.).
Methods:
- CreatePatchDocument(): Converts the operations into a
JsonPatchDocument<T>, which is a format understood by ASP.NET Core´s JSON Patch handler. - GetPropertyByPath(string path): Resolves a property on the object based on the provided JSON Patch path. It handles nested properties and array indices.
Operation
Represents a single patch operation.
Properties:
- op: The operation type (
add,remove,replace). - path: The path to the property to be patched.
- from: (optional) The path from which to copy the value for the operation.
- value: The value to set or remove from the target property.
Example: Full Usage
public class MyObject
{
public string Name { get; set; }
public Address Address { get; set; }
public List<string> Tags { get; set; }
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
}
var patch = new JsonPatch<MyObject>
{
Operations = new List<Operation>
{
new Operation
{
op = "replace",
path = "/Name",
value = "New Name"
},
new Operation
{
op = "replace",
path = "/Address/Street",
value = "New Street"
},
new Operation
{
op = "add",
path = "/Tags/0",
value = "Important"
}
}
};
var patchDoc = patch.CreatePatchDocument();
var myObject = new MyObject
{
Name = "Old Name",
Address = new Address { Street = "Old Street", City = "Old City" },
Tags = new List<string> { "Old Tag" }
};
patchDoc.ApplyTo(myObject);
// myObject will now have:
// Name = "New Name"
// Address.Street = "New Street"
// Tags = ["Important", "Old Tag"]Why Use This Package?
- Type Safety: Ensures that the patch operations are type-safe and apply to the correct properties.
- Flexible Patch Paths: Handles complex paths, including nested properties and array indexing, making it suitable for deep object graphs.
- Extends JSON Patch: While ASP.NET Core´s built-in
JsonPatchDocumenthandles patch operations, this package provides additional functionality for easier creation of patch operations from raw JSON-like data.