I found a library that works: https://github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch

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

Or use patch.JsonPatchDocument.Operations to walk through patch request fields manually.

Answer from whyleee on Stack Overflow
🌐
GitHub
github.com › Morcatko › Morcatko.AspNetCore.JsonMergePatch
GitHub - Morcatko/Morcatko.AspNetCore.JsonMergePatch: JsonMergePatch support for ASP.NET Core
using Morcatko.AspNetCore.JsonMergePatch; [HttpPatch] [Consumes(JsonMergePatchDocument.ContentType)] public void Patch([FromBody] JsonMergePatchDocument<Model> patch) { ... patch.ApplyTo(backendModel); ...
Starred by 98 users
Forked by 23 users
Languages   C# 99.1% | PowerShell 0.9% | C# 99.1% | PowerShell 0.9%
🌐
GitHub
github.com › Morcatko › Morcatko.AspNetCore.JsonMergePatch › blob › master › README.md
Morcatko.AspNetCore.JsonMergePatch/README.md at master · Morcatko/Morcatko.AspNetCore.JsonMergePatch
using Morcatko.AspNetCore.JsonMergePatch; [HttpPatch] [Consumes(JsonMergePatchDocument.ContentType)] public void Patch([FromBody] JsonMergePatchDocument<Model> patch) { ... patch.ApplyTo(backendModel); ...
Author   Morcatko
Top answer
1 of 4
4

You need 3 different states for email value here:

  1. Filled value for update (e.g. [email protected])
  2. null value if email should be removed
  3. Missing value if email should not be touched.

So the problem actually is how to express these 3 states in string property of your model. You can't do this with just raw string property because null value and missing value will conflict as you correctly described. Solution is to use some flag that indicates whether the value was provided in the request. You could either have this flag as another property in your model or create a simple wrapper over string, very similar to Nullable<T> class. I suggest creation of simple generic OptionalValue<T> class:

public class OptionalValue<T>
{
    private T value;
    public T Value
    {
        get => value;

        set
        {
            HasValue = true;
            this.value = value;
        }
    }

    public bool HasValue { get; set; }
}

Then you need custom JsonConverter that could deserialize usual json value to OptionalValue<T>:

class OptionalValueConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(OptionalValue<T>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return new OptionalValue<T>
        {
            Value = (T) reader.Value,
        };
    }

    public override bool CanWrite => false;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Your model will look something like this:

public class SomeModel
{
    public string Surname { get; set; }

    [JsonConverter(typeof(OptionalValueConverter<string>))]
    public OptionalValue<string> Email { get; set; } = new OptionalValue<string>();
}

Note that you assign Email with empty OptionalValue<string>(). If input json does not contains email value than Email property will keep it OptionalValue with HasValue set to false. If input json contains some email, even null, then OptionalValueConverter will create instance of OptionalValue with HasValue set to true.

Now in controller action you could determine any of 3 states for email:

[HttpPatch]
public void Patch([FromBody]SomeModel data)
{
    if (data.Email.HasValue)
    {
        //  Email presents in Json
        if (data.Email.Value == null)
        {
            //  Email should be removed
        }
        else
        {
            //  Email should be updated
        }
    }
    else
    {
        //  Email does not present in Json and should not be affected
    }
}
2 of 4
3

Could you use the JsonMergePatch library? https://github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch

The usage is very simple:

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

It appears to support setting some properties to null, and leaving other properties untouched. Internally, the JsonMergePatchDocument creates a JsonPatch document, with one OperationType.Replace for each item in the request. https://github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch/blob/master/src/Morcatko.AspNetCore.JsonMergePatch/Formatters/JsonMergePatchInputFormatter.cs

🌐
Oracle
docs.oracle.com › en › database › oracle › oracle-database › 19 › adjsn › updating-json-document-json-merge-patch.html
12 Updating a JSON Document with JSON Merge Patch
November 17, 2023 - You can use Oracle SQL function json_mergepatch to update specific portions of a JSON document. You pass it a JSON Merge Patch document, which specifies the changes to make to a specified JSON document. JSON Merge Patch is an IETF standard.
🌐
GitHub
github.com › Morcatko › Morcatko.AspNetCore.JsonMergePatch › issues › 16
Mapping JsonMergePatchDocument when working with DTO · Issue #16 · Morcatko/Morcatko.AspNetCore.JsonMergePatch
January 14, 2019 - It works when map JsonMergePatchDocument.JsonPatchDocument in such way - all operations are mapped.
Author   manifoldhiker
Find elsewhere
🌐
JAXB
javaee.github.io › javaee-spec › javadocs › javax › json › JsonMergePatch.html
JsonMergePatch (Java(TM) EE 8 Specification APIs)
This interface represents an implementation of a JSON Merge Patch as defined by RFC 7396 · A JsonMergePatch can be instantiated with Json.createMergePatch(JsonValue) by specifying the patch operations in a JSON Merge Patch or using Json.createMergeDiff(JsonValue, JsonValue) to create a JSON ...
🌐
GitHub
github.com › moejoe › Moejoe.AspNet.JsonMergePatch
GitHub - moejoe/Moejoe.AspNet.JsonMergePatch: A RFC 7396 JsonMergePatch Implementation for .NET
[HttpPatch] [Route("resources/{resourceId}", Name="PatchResource")] public IHttpActionResult PatchResource(string resourceId, [FromBody] JsonMergePatchDocument<Resource> patchDocument) { var resource = _resourceRepository.Get(resourceId); if(resource == null) return NotFound(); patchDocument.ApplyPatch(resource); resourceRepository.Update(resource); return Request.CreateResponse(HttpStatusCode.NoContent, "No Content"); }
Author   moejoe
🌐
npm
npmjs.com › package › json-merge-patch
json-merge-patch - npm
July 21, 2021 - Implementation of JSON Merge Patch (RFC 7396). Latest version: 1.0.2, last published: 5 years ago. Start using json-merge-patch in your project by running `npm i json-merge-patch`. There are 50 other projects in the npm registry using json-merge-patch.
      » npm install json-merge-patch
    
Published   Jul 21, 2021
Version   1.0.2
🌐
Medium
bijukunjummen.medium.com › json-patch-and-json-merge-patch-quick-example-in-java-c36fdabc2810
Json Patch and Json Merge Patch — Quick Example in Java | by Biju Kunjummen | Medium
May 8, 2021 - Json Patch and Json Merge Patch — Quick Example in Java Json Patch and Json Merge Patch both do one job well — a way to represent a change to a source json structure. Json Patch does it as a …
🌐
DEV Community
dev.to › morcatko › json-merge-patch-in-aspnet-core-1e10
JSON Merge Patch in ASP.NET Core - DEV Community
February 4, 2019 - // Startup.ConfigureServices services .AddMvc() .AddJsonMergePatch(); // Controller [HttpPatch] [Consumes(JsonMergePatchDocument.ContentType)] public void Patch(JsonMergePatchDocument<UserModel> patch) { ... patch.ApplyTo(backendModel); ...
🌐
GitHub
github.com › Morcatko › Morcatko.AspNetCore.JsonMergePatch › issues › 3
Create new instance of JsonMergePatchDocument within the code · Issue #3 · Morcatko/Morcatko.AspNetCore.JsonMergePatch
June 28, 2018 - Hi, thank you for this repo. It's really save a lot of time. I have an issue. I need creat new instance of JsonMergePatchDocument for unit test. But when i create a class list with operation is emp...
Author   Trigun27
🌐
NuGet
nuget.org › packages › Morcatko.AspNetCore.JsonMergePatch
NuGet Gallery | Morcatko.AspNetCore.JsonMergePatch 2.0.5
March 24, 2021 - using Morcatko.AspNetCore.JsonMergePatch; [HttpPatch] [Consumes(JsonMergePatchDocument.ContentType)] public void Patch([FromBody] JsonMergePatchDocument<Model> patch) { ... patch.ApplyTo(backendModel); ...
🌐
RFC Editor
rfc-editor.org › rfc › rfc7386
RFC 7386: JSON Merge Patch
Internet Engineering Task Force (IETF) P. Hoffman Request for Comments: 7386 VPN Consortium Category: Standards Track J. Snell ISSN: 2070-1721 October 2014 JSON Merge Patch Abstract This specification defines the JSON merge patch format and processing rules. The merge patch format is primarily ...
🌐
Githubhelp
githubhelp.com › Morcatko › Morcatko.AspNetCore.JsonMergePatch
The morcatko.aspnetcore.jsonmergepatch from Morcatko - GithubHelp
The Model property of the JsonMergePatchDocument contains the trimmed value for the patch but the PatchDocument contains the non trimmed value which is used to "patch" the object.