JSONPatch is a method of updating documents on an API in a very explicit way. It’s essentially a contract to describe exactly how you want to modify a document (For example, replace the value in a field with another value) without having to also send along the rest of the unchanged values.
What Does A JSON Patch Request Look Like?
The official documentation for JSON Patch lives here : http://jsonpatch.com/, but we’ll do a bit of digging to see how it works inside ASP/C# as not all applications will work. Infact one operation has not yet made it into an official release of ASP.net Core, but we’ll do a quick talk about it anyway.
For all examples, I will be writing JSON Patch requests against an object that looks like so in C# :
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public List<string> Friends { get; set; } }
Patch requests all follow a similar type of structure. It’s a list of “operations” within an array. The operation itself has 3 properties.
“op” – Defines the “type” of operation you want to do. For example add, replace, test etc.
“path” – The “path” of the property on the object you want to edit. In our example above, if we wanted to edit “FirstName” the “path” property would look like “/firstname”
“value” – For the most part, defines the value we want to use within our operation.
Now let’s look at each individual operation.
Add
The Add Operation typically means that you are adding a property to an object or “adding” an item into an array. For the former, this does not work in C#. Because C# is a strongly typed language, you cannot “add” a property onto an object that wasn’t already defined at compile time. game-plinko.app
To add an item into an array the request would look like the following :
{ "op": "add", "path": "/Friends/1", "value": "Mike" }
This would “insert” the value of “Mike” at index 1 in the Friends array. Alternatively you can use the “-” character to insert a record at the end of the array.
{ "op": "add", "path": "/Friends/-", "value": "Mike" }
Remove
Similar to the “Add” operation outlined above, the Remove Operation typically means you are either removing a property from an object or removing an item from an array. But because you can’t actually “remove” a property from an object in C#, what actually happens is that it will set the value to default(T). In some cases if the object is nullable (Or a reference type), it will be set to NULL. But be careful because when used on value types, for example an int, then the value actually gets reset to “0”.
To run Remove on an object property to “reset” it, you would run the following :
{ "op": "remove", "path": "/FirstName"}
You can also run the Remove operation to remove a particular item in an array
{ "op": "remove", "path": "/Friends/1" }
This removes the item from array index 1. There is no such “where” clause for removing or deleting so at times this can seem pretty dangerous to just remove an item from an array index like this since what if the array has changed since we fetched it from the server? There is actually a JSON Patch operation that will help with this, but more on that later in the section below on the “Test” operation.
Replace
Replace does exactly what it says on the tin. It replaces any value for another one. This can work for simple properties on objects :
{ "op": "replace", "path": "/FirstName", "value": "Jim" }
It can also replace particular properties inside an array item :
{ "op": "replace", "path": "/Friends/1", "value": "Bob" }
And it can also replace entire objects/arrays like so :
{ "op": "replace", "path": "/Friends", "value": ["Bob", "Bill"] }
Copy
Copy will move a value from one path to another. This can be a property, object, array etc. In the example below we move the value of the Firstname to the LastName. In practical terms this doesn’t come up a whole lot because you would typically see a simple replace on both properties rather than a copy, but it is possible! If you actually dive into the source code of the ASP.net Core implementation of JSON Patch, you will see that a copy operation simple does an “Add” operation in the background on the path anyway.
{ "op": "copy", "from": "/FirstName", "path" : "/LastName" }
Move
The Move operation is very similar to a Copy, but as it says on the tin, the value will no longer be at the “from” field. This is another one that if you look under the hood of ASP.net Core, it actually does a remove on the from field, and an add on the Path field.
{ "op": "move", "from": "/FirstName", "path" : "/LastName" }
Test
The Test operation does not currently exist in a public release of ASP.net Core, but if you check the source code up on Github, you can see that it’s already being worked on by Microsoft and should make it to the next release. Test is almost a way of doing optimistic locking, or in simpler terms a check to see if the object on the server has changed since we retrieved the data.
Consider the following full patch :
[ { "op": "test", "path": "/FirstName", "value": "Bob" } { "op": "replace", "path": "/FirstName", "value": "Jim" } ]
What this says is, first check that on the path of “/FirstName” that the value is Bob, and if it is, change it to Jim. If the value is not Bob, then nothing will happen. An important thing to note is that you can have more than one Test operation in a single patch payload, but if any one of those Tests fail, then the entire Patch will not be applied.
But Why JSON Patch Anyway?
Obviously a big advantage to JSON Patch is that it’s very lightweight in it’s payload, only sending exactly what’s changed on the object. But there is another big benefit in ASP.net Core, it actually has some great utility for the sole reason that C# is a typed language. It’s hard to explain without a good example. So imagine I request from an API a “Person” object. In C# the model may look like :
public class Person { public string FirstName { get; set; } public string LastName { get; set; } }
And when returned from an API as a JSON object, it looks like so :
{ "firstName" : "James", "lastName" : "Smith" }
Now from the front end, without using JSON Patch, I decide that I only need to update the firstname so I send back the following payload :
{ "firstName" : "Jim" }
Now a question for you when I deserialize this model in C#. Without looking below. What will the values of our model be?
public class Person { public string FirstName { get; set; } //Jim public string LastName { get; set; } //<Null> }
Hmmm. So because we didn’t send through the LastName, it gets serialized to Null. But that’s easy right, we can just ignore values that are null and do something finicky on our data layer to only update the fields we actually passed through. But that’s not necessarily true, what if the field actually is nullable? What if we sent through the following payload :
{ "firstName" : "Jim", "lastName" : null }
So now we’ve actually specified that we want to null this field. But because C# is strongly typed, there is no way for us to determine on the server side that a value is missing from the payload vs when it’s actually been set to null with standard model binding.
This may seem like an odd scenario, because the front end can just always send the full model and never omit fields. And for the most part the model of a front end web library will always match that of the API. But there is one case where that isn’t always true, and that’s mobile applications. Often when submitting a mobile application to say the Apple App Store, it may take weeks to get approved. In this time, you may also have web or android applications that need to be rolled out to utilize new models. Getting synchronization between different platforms is extremely hard and often impossible. While API versioning does go a long way to taking care of this, I still feel JSON Patch has great utility in solving this issue in a slightly different way.
And finally, it’s to the point! Consider the following JSON Patch payload for our Person object :
[ { "op": "replace", "path": "/firstName", "value": "Jim" } ]
This explicitly says that we want to change the first name and nothing else. There is never ambiguity of “did we just forget to send that property or do we actually want it ‘nulled out'”. It’s precise and tells us exactly what’s about to happen.
Adding JSON Patch To Your ASP.net Core Project
Inside Visual Studio, run the following from the Package Manager console to install the official JSON Patch library (It does not come with ASP.net Core in a new project out of the box).
Install-Package Microsoft.AspNetCore.JsonPatch
For this example, I’ll use the following full controller. The main point to note is that the HTTP Verb we use is “Patch”, we accept a type of “JsonPatchDocument<T>” and that to “apply” the changes we simply call “ApplyTo” on the patch and pass in the object we want to update.
[Route("api/[controller]")] public class PersonController : Controller { private readonly Person _defaultPerson = new Person { FirstName = "Jim", LastName = "Smith" }; [HttpPatch("update")] public Person Patch([FromBody]JsonPatchDocument<Person> personPatch) { personPatch.ApplyTo(_defaultPerson); return _defaultPerson; } } public class Person { public string FirstName { get; set; } public string LastName { get; set; } }
In our example we are just using a simple object stored on the controller and updating that, but in a real API we will be pulling the data from a datasource, applying the patch, then saving it back.
When we call this endpoint with the following payload :
[ {"op" : "replace", "path" : "FirstName", "value" : "Bob"} ]
We get the response of :
{ "firstName": "Bob", "lastName": "Smith" }
Awesome! Our first name got changed to Bob! It’s really that simple to get up and running with JSON Patch.
Generating Patches
The first question many people ask is exactly how should they be crafting their JSON Patch payloads. You do not have to do this manually! There are many javascript libraries that can “compare” two objects to generate a patch. And even easier, there are many that can “observe” an object and generate a patch on demand. The JSON Patch website has a great list of libraries to get started : http://jsonpatch.com/
Using Automapper With JSON Patch
The big question I often see around JSON Patch, is that often you are returning View Models/DTOs from your API, and generating patches from there. But then how do you apply those patches back onto a database object? People tend to overthink it and create crazy “transforms” to turn a patch from one object to another, but there is an easier way using Automapper that is extremely simple to get going. The code works like so :
[HttpPatch("update/{id}")] public Person Patch(int id, [FromBody]JsonPatchDocument<PersonDTO> personPatch) { PersonDatabase personDatabase = _personRepository.GetById(id); // Get our original person object from the database. PersonDTO personDTO = _mapper.Map<PersonDTO>(personDatabase); //Use Automapper to map that to our DTO object. personPatch.ApplyTo(personDTO); //Apply the patch to that DTO. _mapper.Map(personDTO, personDatabase); //Use automapper to map the DTO back ontop of the database object. _personRepository.Update(personDatabase); //Update our person in the database. return personDTO; }
An important thing to note is that when mapping the DTO back to the Database object in the second Map call, Automapper has a great function where it can map onto an existing object, and only map fields that it cares about. Any additional fields that are on the database object will be left alone.
If you need help setting up Automapper, check out our great tutorial on Using Automapper In ASP.net Core.
When I run “Install-Package Microsoft.AspNetCore.JsonPatch” I get the error:
Install-Package : Could not install package ‘Microsoft.AspNetCore.JsonPatch 2.0.0’. You are trying to install this package into a project that targets
‘.NETFramework,Version=v4.6.2’, but the package does not contain any assembly references or content files that are compatible with that framework. For more
information, contact the package author.
At line:1 char:1
+ Install-Package Microsoft.AspNetCore.JsonPatch
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Install-Package], Exception
+ FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand
Our Project is using WebAPI 2.0. What am I missing?
It looks like your project is running on full framework and not ASP.net Core (This guide is only for .NET Core).
There is a pre-release version for full framework from Microsoft here : https://www.nuget.org/packages/Microsoft.AspNet.JsonPatch/ . But your mileage may vary (I haven’t used the package personally).
Thanks for the great post.
You say “Test is almost a way of doing optimistic locking”. Why almost?
You’re probably right. It is optimistic locking if you are doing test on a version, timestamp or checksum property.
hi. thanks for your useful post.
i think it is worth mentioning that `.ApplyTo()` method throws a *JsonPatchException* when requested `path`
in one of the requested payloads in JSON request body does not exists in the object that you want to apply it to, and not surrounding it with try/catch block will result in a 500 internal server error response, which is good at all.
i think surrounding this method with a try/catch block and returning a 400 bad request response to client is one proper way of handling it.
Hello, thank you for this article. I have a question. What if I have an Article with three properties Author, Genre, Published and the Published property should only be modified by users in a specific role. What are my options here? I’m looking for a generic and clean way. I don’t want to use if else like structures in my method body. And if possible, I don’t want to create a different endpoint for this property. This is an oversimplified example, but the real environment has many of these properties that need role-based authorization. I was thinking about Custom Attributes for the Dto and then use Custom Authorization filters to check if a user has access. But this is only an idea right now. Would love to get some insights / feedback. Thanks!
That’s actually a pretty good question. Within JsonPatch as it stands in .NET Core, there isn’t really a great way to inspect the patches before they run. I mean you could, but it would probably be messy to sift through them and remove a particular operation based on the role etc.
I think your best option is probably to just apply all patches, and then roll back the ones you need. But actually this is a really good question.
After writing that out. Very unelegant. I guess it’s all going to depend on how often you need to do it.
Whatever solution you come up with, would love to hear of it.
Thank you Wade for you quick response and sharing your ideas about the matter. I will keep your idea of rolling back afterwards, but as you stated it’s not very elegant.
If I come up with something good, I will let you know for sure!
Hi! For ‘remove’ operation you said “There is no such “where” clause for removing or deleting so at times this can seem pretty dangerous to just remove an item from an array index like this since what if the array has changed since we fetched it from the server? There is actually a JSON Patch operation that will help with this, but more on that later.” What did you mean? Because my problem now is that I can’t remove an object in array by object Id. I found out that we can remove element from dictionary by key but in that case we should send a KeyValuePair in ‘add’ operation. So what is possible solution for this issue? Thanks in advance!
Hi Artsiom,
It’s hard to know exactly your needs without the code. But I think (this is a really old post!) I was referring to the “Test” operation in JSONPatch which allows you to check values before you change them. e.g. If you are patching over an object or deleting it, you can check that an ID, Timestamp or Version number matches what you think it should be.
I’m having trouble finding out how to validate the syntax of a patch document in order to return a proper error. I’ve seen where op and path are required but if I misspell or omit path I get a 500 error. If I misspell or omit op it seems to do a replace by default. Am I missing something with patch operation validation?