[DATAREST-1338] JSON patch doesn't update single Map's values Created: 18/Jan/19  Updated: 07/Mar/19  Resolved: 05/Mar/19

Status: Closed
Project: Spring Data REST
Component/s: None
Affects Version/s: None
Fix Version/s: 3.2 M2 (Moore), 3.1.6 (Lovelace SR6)

Type: Bug Priority: Minor
Reporter: Luca Cherubin Assignee: Oliver Drotbohm
Resolution: Fixed Votes: 0
Labels: Json-patch, spring-data-rest
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Last updater: Christoph Strobl
Pull Request URL: https://github.com/spring-projects/spring-data-rest/pull/305


Steps to reproduce:

//Here is my object
public class Book {
    public String author;
    public String ISBN;
    public Map<String, String> characters;

// Here I create a simple instance of the object
Book myBook = new Book();
myBook.author = "Me"
myBook.ISBN = "1234567890"
myBook.characters = new HashMap<>();
myBook.characters.put("protagonist", "Pinco");
myBook.characters.put("antagonist", "Pallo");

Here I create JSON patches

// Here the type of operations that work
    {"op": "replace", "path": "/author", "value": "NewAuthor"},
    {"op": "replace", "path": "/ISBN", "value": 0987654321 },

// I can also modify completely the Map if I want
    {"op": "replace", "path": "/characters", "value": {"protagonist": "Pallo", "antagonist": "Pinco"} }

Here the kind of operations that I would expect to work but do not work

// But I can't update a single value in the map 
{"op": "replace", "path": "/characters/protagonist", "value": "Pallo" }, {"op": "replace", "path": "/characters/antagonist", "value": "Pinco" } 

// I've also tried weird stuff, but doesn't work 
[ {"op": "replace", "path": "/characters[antagonist]", "value": "Pinco"} ]  

Expected result:
I would expect the replace operation on the nested map to work by checking the key.
I think the issue is in the path->SpEL conversion, as the nested fields are automatically converted to properties updates.

Comment by Oliver Drotbohm [ 18/Jan/19 ]

You're right, it's currently a limitation in our to SpEL translations. It's particularly tricky as we have to but by definition cannot statically verify the paths containing a Map traversal as it needs an object instance to be verified on. Our current verification algorithm can safely identify collection traversals as it can inspect numbers. For maps however it's not clear whether a segment is a map key until we bind the path against a type.

It looks like we need to improve TypedSpelPath.verifyPath(…) to not choke on the map key segment and TypedSpelPath(SpelPath, Class<?>) to rebuild the expression based on the type information so that segments are translated into map keys properly.

Comment by Oliver Drotbohm [ 29/Jan/19 ]

Should be back-portable to 3.1 and 3.0 (latter optional).

Comment by mle-enso [ 06/Feb/19 ]

Wonderful, just verified this for our use-case to PATCH an entity like:

// some Lombok annotations
public class Product {
    private String id;
    private Map<Locale, String> names;

with the following change:

[{"op", "remove", "path", "names/fr"}]

…which did not work before.

Comment by Oliver Drotbohm [ 05/Mar/19 ]

That's now merged for inclusion in Spring Data Moore M2 and Lovelace SR6. Backport to Kay unfortunately didn't work as the implementation was using API implemented in Spring Data Commons in Lovelace only.

Generated at Wed Jul 15 09:55:51 UTC 2020 using Jira 8.5.4#805004-sha1:0444eab799707f9ad7b248d69f858774aadfd250.