Skip to content

Commit

Permalink
Updated Tingle.AspNetCore.JsonPatch documentation for Json Merge Patch
Browse files Browse the repository at this point in the history
  • Loading branch information
kennethmugo committed Apr 9, 2024
1 parent 05df29a commit b48922f
Showing 1 changed file with 69 additions and 89 deletions.
158 changes: 69 additions & 89 deletions src/Tingle.AspNetCore.JsonPatch/README.md
Original file line number Diff line number Diff line change
@@ -1,131 +1,111 @@
# Tingle.AspNetCore.JsonPatch

The primary goal of this library is to provide functionalities to perform [JsonPatch](https://tools.ietf.org/html/rfc6902) operations on documents using `System.Text.Json` library. We'll show how to do this in some examples below.
The primary goal of this library is to provide functionalities to perform [Json Patch](https://datatracker.ietf.org/doc/html/rfc6902) and [JSON Merge Patch](https://datatracker.ietf.org/doc/html/rfc7386) operations on documents using `System.Text.Json` library.

Let us first define a class representing a customer with orders.
## Json Patch

JSON patch support is quite similar to Microsoft's equivalent for [Newtonsoft.Json](https://learn.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-8.0). The only difference is how you configure the input and output formatters for all JSON content. This should be done as shown below:

```cs
class Customer
{
public string Name { get; set; }
public List<Order> Orders { get; set; }
public string AlternateName { get; set; }
}
var builder = WebApplication.CreateBuilder(args);

class Order
{
public string Item { get; set; }
public int Quantity { get; set;}
}
```
builder.Services.AddControllers()
.AddJsonPatch() // would be .AddNewtonsoftJson() in the Newtonsoft.Json equivalent
.AddJsonOptions(options => {}); // Add and configure JSON formatters
An instantiated `Customer` object would then look like this:
var app = builder.Build();

```cs
{
"name": "John",
"alternateName": null,
"orders":
[
{
"item": "Toy car",
"quantity": 1
},
{
"item": "C# In A Nutshell Book",
"quantity": 1
}
]
}
```
app.UseHttpsRedirection();

A JSON Patch document has an array of operations. Each operation identifies a particular type of change. Examples of such changes include adding an array element or replacing a property value.
app.UseAuthorization();

Let us create `JsonPatchDocument<Customer>` instance to demonstrate the various patching functionalities we provide.
app.MapControllers();

```cs
var patchDoc = new JsonPatchDocument<Customer>();
// Define operations here...
app.Run();
```

By default, the case transform type is `LowerCase`. Other options available are `UpperCase`, `CamelCase` and `OriginalCase`. These can be set via the constructor of the `JsonPatchDocument<T>`. For our example purposes we'll go with the default casing. Now let us see the supported patch operations.
## Json Merge Patch

## Add Operation
The library helps to deserialize HTTP requests' and responses' JSON body content for merge patch operation. If the merge patch request contains members that appear as null on the target object, those members are added. If the target object contains the member, the value is replaced. Members with null values in the merge patch requests, are removed from the target object (set to null or default).

Let us set the `Name` of the customer and add an object to the end of the `orders` array.
For example, the following JSON documents represent a resource, a JSON Merge Patch document for the resource, and the result of applying the Patch operations.

```cs
var order = new Order
{
Item = "Car tracker",
Quantity = 10
};
### Resource Example

var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Add(x => x.Name, "Ben")
.Add(y => y.Orders, order);
```json
{
"name": null,
"phone": "+254722000000",
"country": "ken"
}
```

## Remove Operation
### JSON Merge Patch Example

Let us set the `Name` to null and delete `orders[0]`
```json
{
"name": "Fabrikam",
"phone": "+254722000001",
"country": null
}
```

```cs
var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Remove(x => x.Name, null)
.Remove(y => y.Orders, 0);
### Resource after patch

```json
{
"name": "Fabrikam",
"phone": "+254722000001",
"country": null
}
```

## Replace Operation
### JSON Merge Patch in ASP.NET Core

This is the same as a `Remove` operation followed by an `Add`. Let us show how to do this below:
Define a `Customer` model:

```cs
var order = new Order
class Customer
{
Item = "Air Fryer",
Quantity = 1
};

var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Replace(x => x.Name, null)
.Replace(y => y.Orders, order, 0);
public string Name { get; set; }
public string Phone { get; set; }
public string Country { get; set; }
}
```

The `Replace` operation can also be used to replace items in a dictionary by the given key.

## Move Operation

Let us Move `orders[1]` to before `orders[0]` and set `AlternateName` from the `Name` value.
Add the following logic in the `program.cs` file. This same logic can be added to `startup.cs`.

```cs
var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Move(x => x.Orders, 0, y => y.Orders, 1) // swap the orders
.Move(x => x.Name, y => y.AlternateName); // set AlternateName to Name while leaving Name as null
```
var builder = WebApplication.CreateBuilder(args);

## Copy Operation
builder.Services.AddControllers()
.AddJsonPatch()
.AddJsonOptions(options => {}); // Add and configure JSON formatters
This operation is fundamentally the same as `Move` without the final `Remove` step.
var app = builder.Build();

Let us in the example below copy the value of `Name` to the `AlternateName` and insert a copy of `orders[1]` before `orders[0]`.
app.UseHttpsRedirection();

```cs
var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Copy(x => x.Orders, 1, y => y.Orders, 0)
.Copy(x => x.Name, y => y.AlternateName);
```
app.UseAuthorization();

## Test Operation
app.MapControllers();

This operation is commonly used to prevent an update when there's a concurrency conflict.
app.Run();
```

The following sample patch document has no effect if the initial value of `Name` is "John", because the test fails:
Use in your controller

```cs
var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Test(x => x.Name, "Andrew")
.Add(x => x.Name, "Ben");
[HttpPatch]
[Consumes(JsonMergePatchDocument.ContentType)]
public void Patch([FromBody] JsonPatchMergeDocument<Customer> patch)
{
...
patch.ApplyTo(customer, ModelState);
...
}
```

The instantiated patch document can then be serialized or deserialized using the `JsonSerializer` in the `System.Text.Json` library.
In a real app, the code would retrieve the data from a store such as a database and update the database after applying the patch.

The preceding action method example calls an overload of `ApplyTo` that takes model state as one of its parameters. With this option, you can get error messages in responses.

0 comments on commit b48922f

Please sign in to comment.