jackson-databind icon indicating copy to clipboard operation
jackson-databind copied to clipboard

ObjectReader support for RFC 7386 Patch Merge

Open robotdan opened this issue 3 years ago • 6 comments

Problem

I want to support JSON Patch Merge on a JSON REST API when using the PUT HTTP Method with a Content-Type of application/merge-patch+json.

The current usage of ObjectMapper.readerForUpdating does perform a merge, but it is not compliant with RFC 7386.

Some control can be had by using the @JsonMerge field annotation, but I believe this simply allows you to control if an array is appended or overwritten with the incoming value used with the ObjectMapper.readerForUpdating reader.

I would be happy to submit a PR (or try) -or assist in any way that I can if this is something you are willing to consider in the library.

Ideal Solution

In my usage, I will know the HTTP Method and the Content Type of the request. Using this information I would like to create a Reader that will use the merge patch rules as defined by RFC 7368.

Example usage to build a reader that is configured for Patch Merge.

// Existing object
User user = userService.retrieveUserById(42);

// New Object Mapper and reader configuration for Merge Patch
ObjectMapper objectMapper = new ObjectMapper();
ObjectReader reader = objectMapper.readerForUpdating(user)
                                  // New method or configuration to turn on merge strategy
                                  .withMergeStrategy(MergeStrategy.RFC_7368);

// Merge in JSON patch 
User merged = reader.readValue(request.getInputStream());

Alternatives

  • Third party libraries (listed below)
  • Perhaps there is a way to accomplish some basic merge / patch with the right combination of Deserialization and Serialization features on the Object Mapper.

Example Code

Using the pseudo code provided in the RFC and tests, I have stubbed out a test that passes. This is not optimal code, but maps the provided pseudo code into some (poor) semblance of Jackson code.

Test: https://github.com/prime-framework/prime-mvc/blob/f4b583fd169608d6bf8f7b0912eae60b0e71acfd/src/test/java/org/primeframework/mvc/PatchMergeTest.java#L109

Business logic for recursive patch merge: https://github.com/prime-framework/prime-mvc/blob/f4b583fd169608d6bf8f7b0912eae60b0e71acfd/src/test/java/org/primeframework/mvc/PatchMergeTest.java#L136

Additional Context

RFC 7396

Other Java libraries that have implemented this specification in part or in full.

  • https://github.com/java-json-tools/json-patch
    • This library has implemented RFC 7396 using Jackson.
  • https://github.com/jeffnelson/jackson-merge-patch
    • This library doesn't look to be a complete drop in solution, but perhaps has some basic support.

Related Issues

https://github.com/FusionAuth/fusionauth-issues/issues/441

robotdan avatar Dec 05 '20 00:12 robotdan

Looks like patch would have to operate on JsonNode? If so, I am not sure separate merge strategy would be necessary or make sense; it might instead be possibly to simply add new merge() method(s) in ObjectReader and ObjectMapper.

Although then again, I wonder if this would be better exposed as something else altogether since it appears to most easily be implemented as operation that takes in 2 JsonNodes, and either modifying one or constructing third one. If so, parsing of JsonNodes can be done separately and it's just merge/patch that is separate thing.

cowtowncoder avatar Dec 05 '20 01:12 cowtowncoder

I would also like to get something like this in place. In addition, it would be very useful to be able to create merge patches and merge diffs with Jackson. The solution above describes implementing merge patch on the server; the next logical step is to be able to call the service effectively from a client. That is what Json.createMergeDiff does, but AFAIK there is no implementation based on Jackson.

erik-wramner avatar Oct 02 '22 17:10 erik-wramner

Yes, PR would be highly welcome! I do not have bandwidth to work on this directly myself, but could help if someone else would like to do so.

cowtowncoder avatar Oct 02 '22 18:10 cowtowncoder

I ended up using https://github.com/java-json-tools/json-patch which has worked pretty well. This library supports both RFC 6902 and 7368.

Our usage is pretty straight forward, we just bind a handler based upon the Content-Type request header, and then handle patch or merge patch. https://github.com/prime-framework/prime-mvc/blob/efb88fd50807c86c0d9a2da33bbc476a26ad304a/src/main/java/org/primeframework/mvc/content/json/JacksonPatchContentHandler.java#L72

@fge or @Capstan any interest in porting your library into a patch here?

robotdan avatar Oct 03 '22 04:10 robotdan

Thank you for update @robotdan!

cowtowncoder avatar Oct 03 '22 16:10 cowtowncoder

I looked at the same library, but it uses a fork of jackson-coreutils and (without spending much time checking) I'm worried that it will cause problems in Quarkus with native builds. Anything not tested and supported by the Quarkus team is risky business if it uses reflection. If that library could be ported into the mainstream (and thus supported in coming Quarkus versions), it would be great.

erik-wramner avatar Oct 08 '22 09:10 erik-wramner