JSON Patch (https://tools.ietf.org/html/rfc6902) defines a JSON document structure for expressing a sequence of operations to apply to a JavaScript Object Notation (JSON) document; it is suitable for use with the HTTP PATCH method. The "application/json-patch+json" media type is used to identify such patch documents. One of the things this can be used for is partial updates for REST-ful API's, or, to quote the IETF: "This format is also potentially useful in other cases in which it is necessary to make partial updates to a JSON document or to a data structure that has similar constraints (i.e., they can be serialized as an object or an array using the JSON grammar)." That's what this package is all about. Web API supports the HttpPatch method, but there's currently no implementation of the JsonPatchDocument in .NET, making it hard to pass in a set of changes that have to be applied - especially if you're working cross-platform and standardization of your API is essential. Have a look at the project site for the current status of this package and to learn how to get started.
$ dotnet add package Tingle.Extensions.JsonPatchThe primary goal of this library is to provide functionalities to perform JsonPatch operations on documents using System.Text.Json library on clients (not server side). We'll show how to do this in some examples below.
Let us first define a class representing a customer with orders.
class Customer
{
public string Name { get; set; }
public List<Order> Orders { get; set; }
public string AlternateName { get; set; }
}
class Order
{
public string Item { get; set; }
public int Quantity { get; set;}
}
An instantiated Customer object would then look like this:
{
"name": "John",
"alternateName": null,
"orders":
[
{
"item": "Toy car",
"quantity": 1
},
{
"item": "C# In A Nutshell Book",
"quantity": 1
}
]
}
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.
Let us create JsonPatchDocument<Customer> instance to demonstrate the various patching functionalities we provide.
var patchDoc = new JsonPatchDocument<Customer>();
// Define operations here
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.
Let us set the Name of the customer and add an object to the end of the orders array.
var order = new Order
{
Item = "Car tracker",
Quantity = 10
};
var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Add(x => x.Name, "Ben")
.Add(y => y.Orders, order);
Let us set the Name to null and delete orders[0]
var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Remove(x => x.Name, null)
.Remove(y => y.Orders, 0);This is the same as a Remove operation followed by an Add. Let us show how to do this below:
var order = new Order
{
Item = "Air Fryer",
Quantity = 1
};
var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Replace(x => x.Name, null)
.Replace(y => y.Orders, order, 0);The Replace operation can also be used to replace items in a dictionary by the given key.
Let us Move orders[1] to before orders[0] and set AlternateName from the Name value.
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 nullThis operation is fundamentally the same as Move without the final Remove step.
Let us in the example below copy the value of Name to the AlternateName and insert a copy of orders[1] before orders[0].
var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Copy(x => x.Orders, 1, y => y.Orders, 0)
.Copy(x => x.Name, y => y.AlternateName);This operation is commonly used to prevent an update when there's a concurrency conflict.
The following sample patch document has no effect if the initial value of Name is "John", because the test fails:
var patchDoc = new JsonPatchDocument<Customer>();
patchDoc.Test(x => x.Name, "Andrew")
.Add(x => x.Name, "Ben");The instantiated patch document can then be serialized or deserialized using the JsonSerializer in the System.Text.Json library.