JsonPatch dans l’API web ASP.NET Core
Cet article explique comment gérer les demandes de correctifs JSON dans une API web ASP.NET Core.
Installation de package
La prise en charge des correctifs JSON dans l’API web ASP.NET Core est basée sur Newtonsoft.Json
et nécessite le package NuGet Microsoft.AspNetCore.Mvc.NewtonsoftJson
. Pour activer la prise en charge des correctifs JSON :
Installez le package NuGet
Microsoft.AspNetCore.Mvc.NewtonsoftJson
.Appelez AddNewtonsoftJson. Par exemple :
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers() .AddNewtonsoftJson(); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
AddNewtonsoftJson
remplace les formateurs d’entrée et de sortie basés par défaut sur System.Text.Json
utilisés pour formater tout le contenu JSON. Cette méthode d'extension est compatible avec les méthodes d'enregistrement de service MVC suivantes :
JsonPatch nécessite de définir Content-Type
en-tête sur application/json-patch+json
.
Ajout de la prise en charge des correctifs JSON lors de l’utilisation de System.Text.Json
Le formateur d’entrée basé sur System.Text.Json
ne prend pas en charge les correctifs JSON. Pour ajouter la prise en charge des correctifs JSON à l’aide de Newtonsoft.Json
, tout en laissant les autres formateurs d’entrée et de sortie inchangés :
Installez le package NuGet
Microsoft.AspNetCore.Mvc.NewtonsoftJson
.Mettez à jour
Program.cs
:using JsonPatchSample; using Microsoft.AspNetCore.Mvc.Formatters; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(options => { options.InputFormatters.Insert(0, MyJPIF.GetJsonPatchInputFormatter()); }); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.Options; namespace JsonPatchSample; public static class MyJPIF { public static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter() { var builder = new ServiceCollection() .AddLogging() .AddMvc() .AddNewtonsoftJson() .Services.BuildServiceProvider(); return builder .GetRequiredService<IOptions<MvcOptions>>() .Value .InputFormatters .OfType<NewtonsoftJsonPatchInputFormatter>() .First(); } }
Le code précédent crée une instance de NewtonsoftJsonPatchInputFormatter et l'insère en tant que première entrée de la collection MvcOptions.InputFormatters. Cet ordre d'inscription garantit que :
NewtonsoftJsonPatchInputFormatter
traite les requêtes de correctifs JSON.- Les gestionnaires d’entrées et de formatage
System.Text.Json
actuels gèrent toutes les autres requêtes et réponses JSON.
Utilisez la méthode Newtonsoft.Json.JsonConvert.SerializeObject
pour sérialiser un fichier JsonPatchDocument.
Méthode de demande HTTP PATCH
Les méthodes PUT et PATCH sont utilisées pour mettre à jour une ressource existante. La différence est que PUT remplace la ressource complète, tandis que PATCH spécifie uniquement les modifications.
Patch JSON
JSON Patch est un format qui spécifie des mises à jour à appliquer à une ressource. Un document JSON Patch possède un tableau des opérations. Chaque opération identifie un type particulier de changement. Des exemples de telles modifications incluent l'ajout d'un élément de tableau ou le remplacement d'une valeur de propriété.
Par exemple, les documents JSON suivants représentent une ressource, un document de correctif JSON pour la ressource et le résultat de l’application des opérations de correctif.
Exemple de ressource
{
"customerName": "John",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
}
]
}
Exemple de correctif JSON
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
Dans le code JSON précédent :
- La propriété
op
indique le type d’opération. - La propriété
path
indique l’élément à mettre à jour. - La propriété
value
fournit la nouvelle valeur.
Ressources après correction
Voici la ressource après l’application du document JSON Patch précédent :
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
Les modifications apportées en appliquant un document de correctif JSON à une ressource sont atomiques. Si une opération de la liste échoue, aucune opération de la liste n'est appliquée.
Syntaxe du chemin
Les différents niveaux de la propriété path d’un objet de l’opération sont séparés par des barres obliques. Par exemple : "/address/zipCode"
.
Les index de base zéro sont utilisés pour spécifier les éléments du tableau. Le premier élément du tableau addresses
serait à /addresses/0
. Pour add
pour terminer un tableau, utilisez un trait d'union (-
) plutôt qu'un numéro d'index : /addresses/-
.
Opérations
Le tableau suivant mentionne les opérations prises en charge telles qu’elles sont définies dans la spécification JSON Patch :
Opération | Notes |
---|---|
add |
Ajouter une propriété ou élément de tableau. Pour la propriété existante : définir la valeur. |
remove |
Supprimer une propriété ou un élément de tableau. |
replace |
Identique à remove suivi de add au même emplacement. |
move |
Identique à remove de la source suivi de add à la destination à l’aide de la valeur de la source. |
copy |
Identique à add à la destination à l’aide de la valeur de la source. |
test |
Retourne le code d’état de réussite si la valeur à path = value fournie. |
Correctifs JSON dans ASP.NET Core
L’implémentation ASP.NET Core de JSON Patch est fournie dans le package NuGet Microsoft.AspNetCore.JsonPatch.
Code de méthode d’action
Dans un contrôleur d’API, une méthode d’action pour JSON Patch :
- est annotée avec l’attribut
HttpPatch
; - Accepte un JsonPatchDocument<TModel>, généralement avec
[FromBody]
. - appelle ApplyTo(Object) sur le document de correctif pour appliquer les modifications.
Voici un exemple :
[HttpPatch]
public IActionResult JsonPatchWithModelState(
[FromBody] JsonPatchDocument<Customer> patchDoc)
{
if (patchDoc != null)
{
var customer = CreateCustomer();
patchDoc.ApplyTo(customer, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return new ObjectResult(customer);
}
else
{
return BadRequest(ModelState);
}
}
Ce code de l'exemple d'application fonctionne avec le modèle Customer
suivant :
namespace JsonPatchSample.Models;
public class Customer
{
public string? CustomerName { get; set; }
public List<Order>? Orders { get; set; }
}
namespace JsonPatchSample.Models;
public class Order
{
public string OrderName { get; set; }
public string OrderType { get; set; }
}
L’exemple de méthode d’action :
- Construit un objet
Customer
. - Applique le correctif.
- Retourne le résultat dans le corps de la réponse.
Dans une application réelle, le code récupérerait les données dans un magasin tel qu’une base de données et mettrait à jour la base de données après avoir appliqué le correctif.
État du modèle
L’exemple de méthode d’action précédent appelle une surcharge de ApplyTo
qui prend l’état du modèle comme un de ses paramètres. Avec cette option, vous pouvez obtenir des messages d’erreur dans les réponses. L’exemple suivant montre le corps d’une demande-réponse incorrecte 400 pour une opération test
:
{
"Customer": [
"The current value 'John' at path 'customerName' != test value 'Nancy'."
]
}
Objets dynamiques
L'exemple de méthode d'action suivant montre comment appliquer un correctif à un objet dynamique :
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);
return Ok(obj);
}
L’opération d’ajout
- Si
path
pointe vers un élément du tableau : insère un nouvel élément avant celui spécifié parpath
. - Si
path
pointe vers une propriété : définit la valeur de propriété. - Si
path
pointe vers un emplacement qui n’existe pas :- Si la ressource à corriger est un objet dynamique : ajoute une propriété.
- Si la ressource à corriger est un objet statique : la demande échoue.
L’exemple de document de correctif suivant définit la valeur de CustomerName
et ajoute un objet Order
à la fin du tableau Orders
.
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
L’opération de suppression
- Si
path
pointe vers un élément du tableau : supprime l’élément. - Si
path
pointe vers une propriété :- Si la ressource à corriger est un objet dynamique : supprime la propriété.
- Si la ressource à corriger est un objet statique :
- Si la propriété est Nullable : met la valeur sur Null.
- Si la propriété n’est pas Nullable : met la valeur sur
default<T>
.
L'exemple de document correctif suivant définit CustomerName
pour la valeur null et supprime Orders[0]
:
[
{
"op": "remove",
"path": "/customerName"
},
{
"op": "remove",
"path": "/orders/0"
}
]
L’opération de remplacement
Cette opération est fonctionnellement identique à remove
suivi de add
.
L'exemple de document correctif suivant définit la valeur de CustomerName
et la remplace Orders[0]
par un nouvel objet Order
:
[
{
"op": "replace",
"path": "/customerName",
"value": "Barry"
},
{
"op": "replace",
"path": "/orders/0",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
L’opération de déplacement
- Si
path
pointe vers un élément du tableau : copie l’élémentfrom
à l’emplacement de l’élémentpath
, puis exécute une opérationremove
sur l’élémentfrom
. - Si
path
pointe vers une propriété : copie la valeur de la propriétéfrom
vers la propriétépath
, puis exécute une opérationremove
sur la propriétéfrom
. - Si
path
pointe vers une propriété qui n’existe pas :- Si la ressource à corriger est un objet statique : la demande échoue.
- Si la ressource à corriger est un objet dynamique : copie la propriété
from
à un emplacement indiqué parpath
, puis exécute une opérationremove
sur la propriétéfrom
.
L’exemple de document de correctif suivant :
- Copie la valeur de
Orders[0].OrderName
versCustomerName
. - Met
Orders[0].OrderName
sur la valeur Null. - Déplace
Orders[1]
avantOrders[0]
.
[
{
"op": "move",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "move",
"from": "/orders/1",
"path": "/orders/0"
}
]
L’opération de copie
Cette opération est fonctionnellement identique à une opération move
sans l’étape remove
finale.
L’exemple de document de correctif suivant :
- Copie la valeur de
Orders[0].OrderName
versCustomerName
. - Insère une copie de
Orders[1]
avantOrders[0]
.
[
{
"op": "copy",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "copy",
"from": "/orders/1",
"path": "/orders/0"
}
]
L’opération de test
Si la valeur à l’emplacement indiqué par path
est différente de la valeur fournie dans value
, la demande échoue. Dans ce cas, toute la demande PATCH échoue, même si toutes les autres opérations dans le document de correctif réussiraient autrement.
L’opération test
est généralement utilisée pour empêcher une mise à jour lorsqu’il y a un conflit d’accès concurrentiel.
L’exemple de document de correctif suivant n’a aucun effet si la valeur initiale de CustomerName
est « John », car le test échoue :
[
{
"op": "test",
"path": "/customerName",
"value": "Nancy"
},
{
"op": "add",
"path": "/customerName",
"value": "Barry"
}
]
Obtenir le code
Affichez ou téléchargez l’exemple de code. (Guide pratique de téléchargement).
Pour tester l’exemple, exécutez l’application et envoyez des demandes HTTP avec les paramètres suivants :
- URL :
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- Méthode HTTP :
PATCH
- En-tête :
Content-Type: application/json-patch+json
- Corps : copiez et collez l’un des exemples de document de correctif JSON à partir du dossier du projet JSON.
Ressources supplémentaires
- Spécification de méthode PATCH IETF RFC 5789 PATCH
- Spécification JSON Patch IETF RFC 6902
- Pointeur JSON IETF RFC 6901
- Documentation JSON Patch. Inclut des liens vers des ressources pour la création de documents JSON Patch.
- Code source ASP.NET Core JSON Patch
Cet article explique comment gérer les demandes de correctifs JSON dans une API web ASP.NET Core.
Installation de package
Pour activer la prise en charge des correctifs JSON dans votre application, procédez comme suit :
Installez le package NuGet
Microsoft.AspNetCore.Mvc.NewtonsoftJson
.Mettez à jour la méthode
Startup.ConfigureServices
du projet pour appeler AddNewtonsoftJson. Par exemple :services .AddControllersWithViews() .AddNewtonsoftJson();
AddNewtonsoftJson
est compatible avec les méthodes d'inscription au service MVC :
Correctifs JSON, AddNewtonsoftJson et System.Text.Json
AddNewtonsoftJson
remplace les formateurs d’entrée et de sortie basés sur System.Text.Json
utilisés pour formater tout le contenu JSON. Pour ajouter la prise en charge des correctifs JSON en utilisant Newtonsoft.Json
, tout en laissant les autres formateurs inchangés, mettez à jour la méthode Startup.ConfigureServices
du projet comme suit :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.InputFormatters.Insert(0, GetJsonPatchInputFormatter());
});
}
private static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter()
{
var builder = new ServiceCollection()
.AddLogging()
.AddMvc()
.AddNewtonsoftJson()
.Services.BuildServiceProvider();
return builder
.GetRequiredService<IOptions<MvcOptions>>()
.Value
.InputFormatters
.OfType<NewtonsoftJsonPatchInputFormatter>()
.First();
}
Le code précédent requiert le package Microsoft.AspNetCore.Mvc.NewtonsoftJson
et les instructions suivantes using
:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Linq;
Utilisez la méthode Newtonsoft.Json.JsonConvert.SerializeObject
pour sérialiser un JsonPatchDocument.
Méthode de demande HTTP PATCH
Les méthodes PUT et PATCH sont utilisées pour mettre à jour une ressource existante. La différence est que PUT remplace la ressource complète, tandis que PATCH spécifie uniquement les modifications.
Patch JSON
JSON Patch est un format qui spécifie des mises à jour à appliquer à une ressource. Un document JSON Patch possède un tableau des opérations. Chaque opération identifie un type particulier de changement. Des exemples de telles modifications incluent l'ajout d'un élément de tableau ou le remplacement d'une valeur de propriété.
Par exemple, les documents JSON suivants représentent une ressource, un document de correctif JSON pour la ressource et le résultat de l’application des opérations de correctif.
Exemple de ressource
{
"customerName": "John",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
}
]
}
Exemple de correctif JSON
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
Dans le code JSON précédent :
- La propriété
op
indique le type d’opération. - La propriété
path
indique l’élément à mettre à jour. - La propriété
value
fournit la nouvelle valeur.
Ressources après correction
Voici la ressource après l’application du document JSON Patch précédent :
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
Les modifications apportées en appliquant un document de correctif JSON à une ressource sont atomiques. Si une opération de la liste échoue, aucune opération de la liste n'est appliquée.
Syntaxe du chemin
Les différents niveaux de la propriété path d’un objet de l’opération sont séparés par des barres obliques. Par exemple : "/address/zipCode"
.
Les index de base zéro sont utilisés pour spécifier les éléments du tableau. Le premier élément du tableau addresses
serait à /addresses/0
. Pour add
pour terminer un tableau, utilisez un trait d'union (-
) plutôt qu'un numéro d'index : /addresses/-
.
Opérations
Le tableau suivant mentionne les opérations prises en charge telles qu’elles sont définies dans la spécification JSON Patch :
Opération | Notes |
---|---|
add |
Ajouter une propriété ou élément de tableau. Pour la propriété existante : définir la valeur. |
remove |
Supprimer une propriété ou un élément de tableau. |
replace |
Identique à remove suivi de add au même emplacement. |
move |
Identique à remove de la source suivi de add à la destination à l’aide de la valeur de la source. |
copy |
Identique à add à la destination à l’aide de la valeur de la source. |
test |
Retourne le code d’état de réussite si la valeur à path = value fournie. |
Correctifs JSON dans ASP.NET Core
L’implémentation ASP.NET Core de JSON Patch est fournie dans le package NuGet Microsoft.AspNetCore.JsonPatch.
Code de méthode d’action
Dans un contrôleur d’API, une méthode d’action pour JSON Patch :
- est annotée avec l’attribut
HttpPatch
; - Accepte un
JsonPatchDocument<T>
, généralement avec[FromBody]
. - appelle
ApplyTo
sur le document de correctif pour appliquer les modifications.
Voici un exemple :
[HttpPatch]
public IActionResult JsonPatchWithModelState(
[FromBody] JsonPatchDocument<Customer> patchDoc)
{
if (patchDoc != null)
{
var customer = CreateCustomer();
patchDoc.ApplyTo(customer, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return new ObjectResult(customer);
}
else
{
return BadRequest(ModelState);
}
}
Ce code de l'exemple d'application fonctionne avec le modèle Customer
suivant :
using System.Collections.Generic;
namespace JsonPatchSample.Models
{
public class Customer
{
public string CustomerName { get; set; }
public List<Order> Orders { get; set; }
}
}
namespace JsonPatchSample.Models
{
public class Order
{
public string OrderName { get; set; }
public string OrderType { get; set; }
}
}
L’exemple de méthode d’action :
- Construit un objet
Customer
. - Applique le correctif.
- Retourne le résultat dans le corps de la réponse.
Dans une application réelle, le code récupérerait les données dans un magasin tel qu’une base de données et mettrait à jour la base de données après avoir appliqué le correctif.
État du modèle
L’exemple de méthode d’action précédent appelle une surcharge de ApplyTo
qui prend l’état du modèle comme un de ses paramètres. Avec cette option, vous pouvez obtenir des messages d’erreur dans les réponses. L’exemple suivant montre le corps d’une demande-réponse incorrecte 400 pour une opération test
:
{
"Customer": [
"The current value 'John' at path 'customerName' is not equal to the test value 'Nancy'."
]
}
Objets dynamiques
L'exemple de méthode d'action suivant montre comment appliquer un correctif à un objet dynamique :
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);
return Ok(obj);
}
L’opération d’ajout
- Si
path
pointe vers un élément du tableau : insère un nouvel élément avant celui spécifié parpath
. - Si
path
pointe vers une propriété : définit la valeur de propriété. - Si
path
pointe vers un emplacement qui n’existe pas :- Si la ressource à corriger est un objet dynamique : ajoute une propriété.
- Si la ressource à corriger est un objet statique : la demande échoue.
L’exemple de document de correctif suivant définit la valeur de CustomerName
et ajoute un objet Order
à la fin du tableau Orders
.
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
L’opération de suppression
- Si
path
pointe vers un élément du tableau : supprime l’élément. - Si
path
pointe vers une propriété :- Si la ressource à corriger est un objet dynamique : supprime la propriété.
- Si la ressource à corriger est un objet statique :
- Si la propriété est Nullable : met la valeur sur Null.
- Si la propriété n’est pas Nullable : met la valeur sur
default<T>
.
L'exemple de document correctif suivant définit CustomerName
pour la valeur null et supprime Orders[0]
:
[
{
"op": "remove",
"path": "/customerName"
},
{
"op": "remove",
"path": "/orders/0"
}
]
L’opération de remplacement
Cette opération est fonctionnellement identique à remove
suivi de add
.
L'exemple de document correctif suivant définit la valeur de CustomerName
et la remplace Orders[0]
par un nouvel objet Order
:
[
{
"op": "replace",
"path": "/customerName",
"value": "Barry"
},
{
"op": "replace",
"path": "/orders/0",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
L’opération de déplacement
- Si
path
pointe vers un élément du tableau : copie l’élémentfrom
à l’emplacement de l’élémentpath
, puis exécute une opérationremove
sur l’élémentfrom
. - Si
path
pointe vers une propriété : copie la valeur de la propriétéfrom
vers la propriétépath
, puis exécute une opérationremove
sur la propriétéfrom
. - Si
path
pointe vers une propriété qui n’existe pas :- Si la ressource à corriger est un objet statique : la demande échoue.
- Si la ressource à corriger est un objet dynamique : copie la propriété
from
à un emplacement indiqué parpath
, puis exécute une opérationremove
sur la propriétéfrom
.
L’exemple de document de correctif suivant :
- Copie la valeur de
Orders[0].OrderName
versCustomerName
. - Met
Orders[0].OrderName
sur la valeur Null. - Déplace
Orders[1]
avantOrders[0]
.
[
{
"op": "move",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "move",
"from": "/orders/1",
"path": "/orders/0"
}
]
L’opération de copie
Cette opération est fonctionnellement identique à une opération move
sans l’étape remove
finale.
L’exemple de document de correctif suivant :
- Copie la valeur de
Orders[0].OrderName
versCustomerName
. - Insère une copie de
Orders[1]
avantOrders[0]
.
[
{
"op": "copy",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "copy",
"from": "/orders/1",
"path": "/orders/0"
}
]
L’opération de test
Si la valeur à l’emplacement indiqué par path
est différente de la valeur fournie dans value
, la demande échoue. Dans ce cas, toute la demande PATCH échoue, même si toutes les autres opérations dans le document de correctif réussiraient autrement.
L’opération test
est généralement utilisée pour empêcher une mise à jour lorsqu’il y a un conflit d’accès concurrentiel.
L’exemple de document de correctif suivant n’a aucun effet si la valeur initiale de CustomerName
est « John », car le test échoue :
[
{
"op": "test",
"path": "/customerName",
"value": "Nancy"
},
{
"op": "add",
"path": "/customerName",
"value": "Barry"
}
]
Obtenir le code
Affichez ou téléchargez l’exemple de code. (Guide pratique de téléchargement).
Pour tester l’exemple, exécutez l’application et envoyez des demandes HTTP avec les paramètres suivants :
- URL :
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- Méthode HTTP :
PATCH
- En-tête :
Content-Type: application/json-patch+json
- Corps : copiez et collez l’un des exemples de document de correctif JSON à partir du dossier du projet JSON.
Ressources supplémentaires
- Spécification de méthode PATCH IETF RFC 5789 PATCH
- Spécification JSON Patch IETF RFC 6902
- Spécification de format de chemin JSON Patch IETF RFC 6901
- Documentation JSON Patch. Inclut des liens vers des ressources pour la création de documents JSON Patch.
- Code source ASP.NET Core JSON Patch