Nancy.JsonPatch
Adds JSON Patch functionality to Nancy
Install / Use
/learn @DSaunders/Nancy.JsonPatchREADME
Nancy.JsonPatch 
Adds JSON Patch functionality to Nancy
Patch["/customer/{customerId}"] = _ =>
{
Customer customer = _repository.Get(_.customerId);
if (this.JsonPatch(customer))
_repository.Save(customer);
return HttpStatusCode.NoContent;
};
This is an example of a JSON Patch document:
[
{ "op": "test", "path": "/a/b/c", "value": "foo" },
{ "op": "remove", "path": "/a/b/c" },
]
For a good introduction to JSON Patch, checkout jsonpatch.com or RFC6902.
Installation
Install via NuGet:
PM > Install-Package Nancy.JsonPatch
Getting Started
Nancy.JsonPatch adds an extension method to NancyModule, so usage is as simple as calling:
this.JsonPatch(myThing);
from anywhere inside your route action.
Nancy.JsonPatch will then deserialize the request body and apply the patch operations to your object.
RFC6902 states that if one operation in the document fails, the entire document should not be considered successful. In other words, you only want to save the changes to your object if all operations in the document succeed.
The .JsonPatch() extension method returns a JsonPatchResult indicating the result of the patch. This can be implicitly converted to a bool, allowing you to do this:
if (this.JsonPatch(myThing))
_repository.Save(myThing);
Advanced Error Handling
RFC5789 describes various error codes that should be returned depending on the result of the JSON Patch operation.
If you wish to handle failure in this way, the JsonPatchResult object returned from .JsonPatch() should give you the information you need:
public class JsonPatchResult
{
// True if the operation succeeds, otherwise false
public bool Succeeded { get; set; }
// In the event that an operation fails, this will contain a message describing the failure
public string Message { get; set; }
// Can be either:
// TestFailed - A 'test' operation failed
// CouldNotParseJson - The JSON Patch document in the request body is invalid
// CouldNotParsePath - A 'path' could not be mapped to a property on the object being patched
// CouldNotParseFrom - As above, but for the 'from' path used for copy/move operations
// OperationFailed - An operation in the document could not be completed (see the Message for details)
public JsonPatchFailureReason FailureReason { get; set; }
}
For example:
var patchResult = this.JsonPatch(myThing);
if (!patchResult)
{
if (patchResult.FailureReason == JsonPatchFailureReason.CouldNotParsePath)
return HttpStatusCode.Conflict;
else
return HttpStatusCode.UnprocessableEntity;
}
_repository.Save(myThing);
return HttpStatusCode.NoContent;
JSON Patch operations in details
Since C# is strongly typed, our implementation has to be slightly different from that described in RFC6902.
This is how Nancy.JsonPatch implements each JsonPatch operation:
Add
[
{ "op": "add", "path": "/Name", "value": "Nancy" }
]
If the 'path' refers to a specific item in a collection (e.g. '/SomeCollection/2'), the item is added before that item.
If the 'path' uses '-', to refer to the end of the collection (e.g '/SomeCollection/-') the item is added to the end of the existing collection.
We can't add properties to objects in .NET. Therefore if the 'path' refers to a property that is not a collection (e.g. '/FirstName'), Add is treated as Replace.
Remove
[
{ "op": "remove", "path": "/Name" }
]
For collections, removes the item from the collection.
Since we can't remove properties from strongly-typed objects, if the 'path' does not refer to a collection then the target property will be set to it's default value (e.g. null for reference types)
Replace
[
{ "op": "replace", "path": "/SomeCollection/1", "value": { "FirstName" : "Nancy" } }
]
Replaces the property referred to by the 'path' with the new 'value'.
Copy
[
{ "op": "copy", "path": "/Customer/1", "from": "/TopCustomer" }
]
Copies the value of one property to another.
Performs an add operation, taking the value of the item in the 'from' path and adding to the object in referred to by 'path'.
Move
[
{ "op": "move", "path": "/Customer/1", "from": "/TopCustomer" }
]
Moves the value of one property to another.
The same as copy, but performs a remove operation on the original 'path' first.
Test
[
{ "op": "test", "path": "/Customer/1/FirstName", "value": "Nancy" }
]
Tests that the value of the property referred to by the 'path' matches that specified in 'value'. If the test fails, execution of the JSON Patch document stops and an error is returned from the .JsonPatch() method.
So is this fine to use right now?
Yeah! If you do find any issues though, please let me know (or, even better, submit a pull request :smile: ).
Related Skills
node-connect
338.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
338.7kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.6kCommit, push, and open a PR
