Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create a JsonPatchDocument from comparing two c# objects?

Tags:

Given I have two c# objects of the same type, I want to compare them to create a JsonPatchDocument.

I have a StyleDetail class defined like this:

public class StyleDetail     {         public string Id { get; set; }         public string Code { get; set; }         public string Name { get; set; }         public decimal OriginalPrice { get; set; }         public decimal Price { get; set; }         public string Notes { get; set; }         public string ImageUrl { get; set; }         public bool Wishlist { get; set; }         public List<string> Attributes { get; set; }         public ColourList Colours { get; set; }         public SizeList Sizes { get; set; }         public ResultPage<Style> Related { get; set; }         public ResultPage<Style> Similar { get; set; }         public List<Promotion> Promotions { get; set; }         public int StoreStock { get; set; }         public StyleDetail()         {             Attributes = new List<string>();             Colours = new ColourList();             Sizes = new SizeList();             Promotions = new List<Promotion>();         }     } 

if I have two StyleDetail objects

StyleDetail styleNew = db.GetStyle(123); StyleDetail styleOld = db.GetStyle(456); 

I now want to create a JsonPatchDocument so I can send the differences to my REST API... How to do this??

JsonPatchDocument patch = new JsonPatchDocument(); // Now I want to populate patch with the differences between styleNew and styleOld - how? 

in javascript, there is a library to do this https://www.npmjs.com/package/rfc6902

Calculate diff between two objects:

rfc6902.createPatch({first: 'Chris'}, {first: 'Chris', last: 'Brown'});

[ { op: 'add', path: '/last', value: 'Brown' } ] 

but I am looking for a c# implementation

like image 350
jmc Avatar asked Apr 29 '17 05:04

jmc


People also ask

How do I test a JSON Patch document sample?

To test the sample, run the app and send HTTP requests with the following settings: Body: Copy and paste one of the JSON patch document samples from the JSON project folder. JSON Patch documentation. Includes links to resources for creating JSON Patch documents. This article explains how to handle JSON Patch requests in an ASP.NET Core web API.

How do I serialize a jsonpatchdocument?

The preceding code requires the Microsoft.AspNetCore.Mvc.NewtonsoftJson package and the following using statements: Use the Newtonsoft.Json.JsonConvert.SerializeObject method to serialize a JsonPatchDocument. The PUT and PATCH methods are used to update an existing resource.

What is the difference between path and value in JSON Patch?

The path property indicates the element to update. The value property provides the new value. The changes made by applying a JSON Patch document to a resource are atomic. If any operation in the list fails, no operation in the list is applied. If path points to an array element: removes the element.

How to implement JSON Patch in ASP NET Core?

The ASP.NET Core implementation of JSON Patch is provided in the Microsoft.AspNetCore.JsonPatch NuGet package. The package is included in the Microsoft.AspnetCore.App metapackage. Is annotated with the HttpPatch attribute. Accepts a JsonPatchDocument<T>, typically with [FromBody]. Calls ApplyTo on the patch document to apply the changes.


1 Answers

Let's abuse the fact that your classes are serializable to JSON! Here's a first attempt at a patch creator that doesn't care about your actual object, only about the JSON representation of that object.

public static JsonPatchDocument CreatePatch(object originalObject, object modifiedObject) {     var original = JObject.FromObject(originalObject);     var modified = JObject.FromObject(modifiedObject);      var patch = new JsonPatchDocument();     FillPatchForObject(original, modified, patch, "/");      return patch; }  static void FillPatchForObject(JObject orig, JObject mod, JsonPatchDocument patch, string path) {     var origNames = orig.Properties().Select(x => x.Name).ToArray();     var modNames = mod.Properties().Select(x => x.Name).ToArray();      // Names removed in modified     foreach (var k in origNames.Except(modNames))     {         var prop = orig.Property(k);         patch.Remove(path + prop.Name);     }      // Names added in modified     foreach (var k in modNames.Except(origNames))     {         var prop = mod.Property(k);         patch.Add(path + prop.Name, prop.Value);     }      // Present in both     foreach (var k in origNames.Intersect(modNames))     {         var origProp = orig.Property(k);         var modProp = mod.Property(k);          if (origProp.Value.Type != modProp.Value.Type)         {             patch.Replace(path + modProp.Name, modProp.Value);         }         else if (!string.Equals(                         origProp.Value.ToString(Newtonsoft.Json.Formatting.None),                         modProp.Value.ToString(Newtonsoft.Json.Formatting.None)))         {             if (origProp.Value.Type == JTokenType.Object)             {                 // Recurse into objects                 FillPatchForObject(origProp.Value as JObject, modProp.Value as JObject, patch, path + modProp.Name +"/");             }             else             {                 // Replace values directly                 patch.Replace(path + modProp.Name, modProp.Value);             }         }            } } 

Usage:

var patch = CreatePatch(     new { Unchanged = new[] { 1, 2, 3, 4, 5 }, Changed = "1", Removed = "1" },     new { Unchanged = new[] { 1, 2, 3, 4, 5 }, Changed = "2", Added = new { x = "1" } });  // Result of JsonConvert.SerializeObject(patch) [   {     "path": "/Removed",     "op": "remove"   },   {     "value": {       "x": "1"     },     "path": "/Added",     "op": "add"   },   {     "value": "2",     "path": "/Changed",     "op": "replace"   } ] 
like image 112
gnud Avatar answered Oct 17 '22 08:10

gnud