30

I have the following JSON document, from which I want to remove the "roleId2" element from the "roles" field's array value:

{
  "id" : 12345,
  "firstName": "SomeFirstName",
  "lastName": "SomeLastName",
  "roles":["roleId1", "roleId2", "roleId3"]
}

How can I write a JSON Patch document to remove that element? Is the following expression valid?

{"op": "remove", "path":"/roles", "value": "roleId2"}

Or, should it look like this (because the "roles" value in the document is an array)?

{"op": "remove", "path":"/roles", "value": ["roleId2"]}

From reading RFC 6902, it is not clear to me which—if either—is correct. The RFC mentions the following behavior, but I'm not sure if it's relevant here.

If removing an element from an array, any elements above the specified index are shifted one position to the left.

Community
  • 1
  • 1
Niranjan
  • 2,601
  • 8
  • 43
  • 54

2 Answers2

16

The correct patch to remove item at index 1 from the array is:

{"op": "remove", "path": "/roles/1"}

See working example at JSFiddle (using Fast-JSON-Patch)

warpech
  • 6,293
  • 4
  • 35
  • 36
  • 1
    Yea, I think this will work. However, can you validate if following understanding of mine is correct? **if someone wants to remove multiple elements from the array, they have to provide multiple remove OPs in the patch array.** – Niranjan Dec 13 '14 at 21:00
  • 1
    Yes. To see more usage examples for JSON Patch, you can study this repo: https://github.com/json-patch/json-patch-tests. It has 84 tests that show almost every possible kind of patch – warpech Dec 15 '14 at 12:41
  • 41
    I think this is a very poor solution, because what happens when another client has removed "roleId1" just before? Then you're unintentionally gonna remove "roleId3" instead of "roleId2". You might solve this by adding an explicit test operation before, but ideally the default situation should be more safe and also ideally you would want (in many cases at least) the patch to succeed even if the index has changed, but the value can still be removed. – arendjr May 19 '15 at 11:23
  • 1
    @warpech : is there a way we can remove using value name of the object instead of index? because in my case objects are created in backed and I dont know the index for that. – priyank Dec 07 '16 at 07:20
  • @Niranjan: need input from you as well on my above query. Thanks – priyank Dec 07 '16 at 07:21
  • @priyank, I did not implement patch yet in my application because security team was having some concerns for this. We will revisit sometimes later (hopefully!). That said, as per https://tools.ietf.org/html/rfc6902#appendix-A.4 array elements can be removed only via indexes. Currently I also have similar case as yours; so, I am not sure how to achieve this abiding to the RFC (to me it is not possible). I think we need to use some unconventional approach here. – Niranjan Dec 07 '16 at 07:40
  • @arendjr Even if you attach a `test` operation, you cannot achieve this easily unless you lock the resource. What if that resource is used from two service endpoints? I think it's easier to rely on the optimistic locking feature of the backend store (e.g. CouchBase has Check-And-Set) and let one of the request fail. Is my understanding incorrect? Another question on your statement `ideally you would want (in many cases at least) the patch to succeed even if the index has changed, but the value can still be removed.`. How can you achieve this? – Niranjan Dec 07 '16 at 07:48
  • @Niranjan: Yes, if you were to add a test, the backend needs to treat that test atomically with the operation. Locking / Check-And-Set are approaches to achieve that. – arendjr Dec 08 '16 at 08:50
  • 1
    @Niranjan: Regarding removing the value, it would require a different syntax altogether. Maybe something like: `{"op": "remove-value", "path": "/roles", "value": "roleId2"}` That way "roleId2" can be removed regardless of its index. You could even consider removing a non-existing value to always be successful, so that when two clients simultaneously try to remove the same role, they'll both get success responses. – arendjr Dec 08 '16 at 08:53
  • @arendjr is this syntax available because I think "remove-value" operation is not supported for JSON patch. I am asking this as I also have similar use case where we need to remove value by name instead of index. thanks – priyank Dec 09 '16 at 05:41
  • @priyank No, it was an example of what could be done, but AFAIK there's no official solution for that, so you'd need to create your own extension to support that. – arendjr Dec 10 '16 at 07:45
  • And how do we know the index? – Dave Sep 26 '17 at 13:27
  • @arendjr _what happens when another client has removed "roleId1" just before?_ Who is "another client"? The data-to-patch must be the same as the data which the patch was created from. You can not modify the data how you want and then expect the patch to work. – Petr Újezdský Mar 31 '22 at 14:44
  • @PetrÚjezdský Sure, you can take that position, but that’s just saying JSON Patch is unsuitable to be used with REST APIs. For those that do care about that use case, it’s reasonable to want to relax that requirement and wish for a solution that is idempotent. – arendjr Apr 01 '22 at 15:23
  • _unsuitable to be used with REST APIs_ what do you mean by that? We use it in REST API. We simply add `version` property along with each patch to ensure we patch the proper (-1) version of the object. If versions mismatch, client requests the whole object (same as at the beginning of the communication). – Petr Újezdský Apr 01 '22 at 20:47
  • Btw even if you implement the "value-based" patching, what if the array contains 2 *similar* objects (users, at indexes 12 and 13) having `name: 'Peter'` attribute. Then someone silently removes the object at index 12 so the 13th takes it place. And now you send the patch to modify `name` at position 12 with your value-based patch having test to value "Peter". This way you change someone else's name! You would have to test against the WHOLE object to mitigate this and that would be veery expensive and data consuming. – Petr Újezdský Apr 01 '22 at 20:55
11

This is not supported by the RFC 6902. A possibile revision to the JSON-Patch format is being discussed, which may support value-based array operations.

Petr Gotthard
  • 343
  • 4
  • 11
  • 1
    @Nick It *is* an answer. The answer is "it's not supported, but it's being discussed as as a revision." Plus, even if the links go down, there's enough information in the post for someone to find out more information - you could just Google "RFC 6902", for example. – EJoshuaS - Stand with Ukraine Nov 18 '19 at 23:05
  • 1
    @Nick The official standard for when something's a link-only answer can be found [here](https://meta.stackexchange.com/questions/225370/your-answer-is-in-another-castle-when-is-an-answer-not-an-answer). This *is* an answer because it has information for readers to go on even if the links "go dead." Even if the link to RFC 6902 goes dead, readers still have information about what standard document applies, and it's quite easy for them to use that to find out more information. – EJoshuaS - Stand with Ukraine Nov 18 '19 at 23:12
  • Um, what? It is specifically listed in A.4 section of the linked document. There is also example very similar to what the OP wants. https://www.rfc-editor.org/rfc/rfc6902#appendix-A.4 – Petr Újezdský Mar 31 '22 at 14:50
  • 2
    @PetrÚjezdský The section A.4 talks about "index-based operations" (i.e. to remove a second element regardless its value). The question and this answer talk about "value-based operations" (i.e. to remove a specific value without knowing its index). Of course, index-based operations are possible as you correctly point out. – Petr Gotthard Apr 01 '22 at 08:29
  • Thanks for clarification, I have looked at it from different perspective to simply achieve the goal - to create the functional patch :) Btw the value-based operations seems to have some traction, see https://github.com/json-patch/json-patch2/issues/12#issuecomment-541363554. Btw I support his opinion to not implement it :) – Petr Újezdský Apr 01 '22 at 10:06