DEV Community

Morcatko
Morcatko

Posted on

JSON Merge Patch in ASP.NET Core

REST is well-known and commonly used pattern for creating web APIs. There are many HTTP verbs, but the most frequently used for building REST services are:

  • POST - create new item
  • GET - read item
  • PUT - update/replace item
  • DELETE - delete item

These methods are usually enough for a client to make all operations with a resource. But what if the client wants to change only particular properties of a resource?
For demonstration scenario lets say we have following object and we want to change the weapon property to "Knife"

{
    name: "James Bond"
    age: "45"
    weapon: "Gun"
}

Client code in pseudo-C#:

srcJson = GET {resourceUrl}
model = Json.Parse<UserModel>(srcJson)
model.weapon = "Knife"
dstJson = Json.Serialize(model)
PUT {resourceUrl}

That is the obvious solution - GET the resource, modify it and PUT it back to a server. Depending on your architecture and used languages this might have some drawbacks.
Imagine what happens when your UserModel is missing some properties. They will get lost and not send to the server in a PUT call. In case of default ASP.NET Core/C# setup they will be interpreted as a change to default values leading to a data loss.

JSON Patch

PUT call replaces whole object rather then performing partial resource update only. JSON Patch was created to modify the resource exactly as you want by sending list of operations rather then the object itself.

JSON PATCH request

[
 { op: "replace", path: "/weapon", value: "Knife" }
]

Implementation of JSON Patch is part of ASP.NET Core,
This structure allows full control of all properties of a resource including arrays, but also tends to be quite chatty and not very human writeable/readable.

JSON Merge Patch

If you are looking for something simpler then JSON Patch, there is other way.
JSON Merge Patch allows you to send changes in the form of original model. There is not a list of operations, but rather a patch object containing only changes that are merged with original resource on the server

JSON Merge patch

{
    weapon: "Knife"
}

And the result will be

{
    name: "James Bond"
    age: "45"
    weapon: "Knife"
}

JSON Merge patch is limited compared to full JSON Patch. You cannot remove a property (as missing property means "no change" rather then "remove") and also array operations are limited.

Morcatko.AspNetCore.JsonMergePatch

Using JSON Merge Patch in C# is not easy. C# is statically typed language and JSON Merge Patch format expects the exact opposite - it defines the operations by property (non)existence.
Morcatko.AspNetCore.JsonMergePatch is my implementation of JSON Merge Patch for ASP.NET Core. It is designed to fit nicely into ASP.NET Core architecture. Its API is same as API of full JSON patch. All you have to do to use it is to initialize it in your startup class and make an endpoint that accepts patch object.

// Startup.ConfigureServices
services
    .AddMvc()
    .AddJsonMergePatch();

// Controller
[HttpPatch]
[Consumes(JsonMergePatchDocument.ContentType)]
public void Patch(JsonMergePatchDocument<UserModel> patch)
{
    ...
    patch.ApplyTo(backendModel);
    ...
}

That is it.
Notice the Consumes attribute. You can use it together with full JSON Patch. Requests ContentType is a way how to distinquish betweens both versions.

In case you are using swagger to generate API documentation for your service you will need to add a DocumentFilter to your swagger configuration - see github for more details

Morcatko / Morcatko.AspNetCore.JsonMergePatch

JsonMergePatch support for ASP.NET Core

JSON Merge Patch support for ASP.NET Core 2.x

JSON Merge Patch

  • RFC 7396
  • performs partial resource update similar to JSON Patch
  • Supports Swagger
  • netstandard 2.0
C# object
var backendModel = new Model()
{
    Name = "James Bond"
    Age = "45"
    Weapon = "Gun"
}
JSON Merge Patch
{
    "Weapon": "Knife"
}

resulting C# object:
{
    Name = "James Bond"
    Age = "45"
    Weapon = "Knife"
}

How to

See testApp2.0 for sample

  1. Install Morcatko.AspNetCore.JsonMergePatch nuget
  2. Add to your startup class
using Morcatko.AspNetCore.JsonMergePatch;

public void ConfigureServices(IServiceCollection services)
{
    ...
    services
        .AddMvc()
        .AddJsonMergePatch();
    ...
}
  1. Use in your controller
using Morcatko.AspNetCore.JsonMergePatch;

[HttpPatch]
[Consumes(JsonMergePatchDocument.ContentType)]
public void Patch([FromBody] JsonMergePatchDocument<Model> patch)
{
    ...
    patch.ApplyTo(backendModel);
    ...
}
  1. Swagger config (optional)

copy & paste this class into your app - https://github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch/blob/master/test/testApp2.0/JsonMergePatchDocumentOperationFilter.cs

services.AddSwaggerGen(c =>
    {
        c.OperationFilter<JsonMergePatchDocumentOperationFilter>();
    });

How to - unit testing

See Morcatko.AspNetCore.JsonMergePatch.Tests.Builder.Json.Simple class for more examples

Morcatko.AspNetCore.JsonMergePatch.Tests.Builder.Json
public void UnitTest()
{
    var model = new Model();

Top comments (0)