Most active commenters
  • skrebbel(4)

←back to thread

JSON Patch

(zuplo.com)
299 points DataOverload | 20 comments | | HN request time: 0.396s | source | bottom
1. skrebbel ◴[] No.41881933[source]
I quite like JSON Patch but I've always felt that it's so convoluted only because of its goal of being able to modify every possible JSON document under the sun. If you allow yourself to restrict your data set slightly, you can patch documents much simpler.

For example, Firebase doesn't let you store null values. Instead, for Firebase, setting something to null means the same as deleting it. With a single simple restriction like that, you can implement PATCH simply by accepting a (recursive) partial object of whatever that endpoint. Eg if /books/1 has

    { title: "Dune", score: 9 }
you can add a PATCH /books/1 that takes eg

    { score: null, author: "Frank Herbert" }
and the result will be

    { title: "Dune", author: "Frank Herbert" }
This is way simpler than JSON Patch - there's nothing new to learn, except "null means delete". IMO "nothing new to learn" is a fantastic feature for an API to have.

Of course, if you can't reserve a magic value to mean "delete" then you can't do this. Also, appending things to arrays etc can't be done elegantly (but partially mutating arrays in PATCH is, I'd wager, often bad API design anyway). But it solves a very large % of the use cases JSON Patch is designed for in a, in my humble opinion, much more elegant way.

replies(7): >>41882045 #>>41882475 #>>41883360 #>>41886201 #>>41886934 #>>41887163 #>>41887172 #
2. gregwebs ◴[] No.41882045[source]
The article has a section at the bottom "Alternatives..." [1]. It links to "JSON Merge Patch" which is what you are describing: https://zuplo.com/blog/2024/10/11/what-is-json-merge-patch

That's the format that people tend to naturally use. The main problem is that arrays can only be replaced.

[1] https://zuplo.com/blog/2024/10/10/unlocking-the-power-of-jso...

replies(4): >>41882087 #>>41882187 #>>41883145 #>>41885477 #
3. skrebbel ◴[] No.41882087[source]
Nice! I gotta say I didn't expect a thing called "JSON Merge Patch" to be simpler and more concise than a thing called "JSON Patch" :-)
replies(1): >>41882397 #
4. gitaarik ◴[] No.41882187[source]
Also the first thing I was thinking. The only reason I can see for using JSON Patch is for updating huge arrays. But I never really had such big arrays that I felt the necessity for something like this.
5. DataOverload ◴[] No.41882397{3}[source]
Same here, I actually made a tool for this called www.jsonmergepatch.com - give it a try
6. enragedcacti ◴[] No.41882475[source]
I wonder if adding a merge op would be a viable option e.g.:

    { "op": "merge", "path": "/", "value": { "score": null, "author": "Frank Herbert" } }
It's kind of nice to retain the terse and intuitive format while also gaining features like "test" and explicit nulls. It's of course not spec compliant anymore but for standard JSON Patch APIs the client could implement a simple Merge Patch->Patch compiler.
7. jmull ◴[] No.41883145[source]
json merge patch is pretty good. I think it just needs an optional extension to specify an alternative magical value for “delete”. null is a pretty good default, and comports well with typical database patterns, but is outright bad for some things.

I think it also needs a “replace” option at the individual object update level. Merge is a good default, but the semantics of the data or a particular update could differ.

You’re almost surely doing something wrong if replace doesn’t work for arrays. I think the missing thing is a collection that is both ordered and keyed (often not by the same value). JSON by itself just doesn’t do that.

So maybe what’s missing is a general facility for specifying metadata on an update, which can be used to specify the magical delete value, and the key/ordering field for keyed, ordered collections.

replies(2): >>41883299 #>>41886076 #
8. throwway120385 ◴[] No.41883299{3}[source]
> You’re almost surely doing something wrong if replace doesn’t work for arrays. I think the missing thing is a collection that is both ordered and keyed (often not by the same value). JSON by itself just doesn’t do that.

Yeah, you could assign an identity value to each element of the array and then use a subresource to manipulate those elements by identity value. Then you could PUT using the same JSON merge mechanism to clear individual fields, and you could DELETE to remove items from the array by subresource.

This just seems like a reinvention of a crufty piece of XML.

replies(1): >>41888784 #
9. dap ◴[] No.41883360[source]
> I quite like JSON Patch but I've always felt that it's so convoluted only because of its goal of being able to modify every possible JSON document under the sun.

It seems like this is mainly a problem if you're implementing this _ad hoc_ on the client or server side -- is that right?

I mean: presumably most of the time that you want to either of these, you already have both the old and new object, right? Is it not straightforward to write a function (or library) that takes two plain objects and generates the JSON Patch from one to the other, and then use that everywhere and not think about this (but retain the advantage of "being able to modify every possible JSON document under the sun").

If there are cases where you're making a delta without the original object (i.e., I know I always want to remove one field and add some other, whatever the original state was), it seems like you could have nice helpers like `JsonPatch::new().remove_field('field1').add_field('field2', value)`.

I haven't actually done this so maybe I'm missing something about how you want to use these things in practice?

edit to add my motivation: I'd much rather having something robust and predictable, even if it means writing tooling to make it convenient, than something that _seems_ easy but then can't handle some cases or does something different than expected ("I wanted this to be null, not gone!").

replies(2): >>41883679 #>>41886066 #
10. crabmusket ◴[] No.41883679[source]
Immer can generate patches from your changes: https://immerjs.github.io/immer/patches

I think I've seen this in other libraries too but I forget which.

11. gleenn ◴[] No.41885477[source]
What if you represented arrays as recursively nested triples like '(1 2 3 4 5) as [[1 null 2] 3 [4 null 5]]. Then you could parch the tree of triples much more succinctly. You might have to disallow nested arrays but this would as bad a restriction as disallowing nulls as map values. You could append and delete array indexes better. Or maybe make it an 8-way tree like Clojure does for its vector representation to condense the patch further.
12. skrebbel ◴[] No.41886066[source]
> I mean: presumably most of the time that you want to either of these, you already have both the old and new object, right?

Hm, maybe? I'm thinking about this from the perspective API design, eg for REST APIs, JavaScript modules etc. How to let users change a single field in an object, and leave the rest alone? Like set the subject of a conversation? JSON Patch lets you do that, and so does this. And whether you have the target object already is kind of.. maybe? Sometimes? I wouldn't make that assumption tbh.

13. skrebbel ◴[] No.41886076{3}[source]
Once you add all that, it loses "no need to learn something new". At that point, I think I'd just go with JSON Patch which solves all of these, and more.
14. _kidlike ◴[] No.41886201[source]
Isn't this just application/merge-patch+json?

(RFC 7396)

15. revskill ◴[] No.41886934[source]
There's no real delete, just add a status: archived for "deletion" and you 're fine, nothing else to learn.
16. zaxomi ◴[] No.41887163[source]
> appending things to arrays etc can't be done elegantly

Are you referring to the possibility to point to the end of the array? If so, a single minus sign might solve it: "/path/to/the/array/-"

RFC 6901 JavaScript Object Notation (JSON) Pointer > exactly the single character "-", making the new referenced value the (nonexistent) member after the last array element

17. ndr ◴[] No.41887172[source]
How does it handle arrays/repeated fields?

Eg: how do you update a field of the third element on an array when there are 5 elements in an array?

The same that in `jq` would be:

`jq ".people[2].age |= 50" example.json`

replies(1): >>41887471 #
18. bpicolo ◴[] No.41887471[source]
You overwrite the entire array. Replacement is a safe, idempotent operation.

If you're concerned about concurrency for array updates, you'll usually need a form of concurrency control whether the database supports partial updates or not.

replies(1): >>41904589 #
19. jmull ◴[] No.41888784{4}[source]
I just mean a mechanism for specifying metadata, and a little metadata.

Off the top of my head, an optional header like "MergeMetadataObjectPropertyName: @mergeMetadata"

Which would cause objects in the merge containing the property "@mergeMetadata" to be treated specially.

The merge meta data could (optionally) specify an alternative to null for the special delete value. Or (optionally) specify the key value for an array representing an ordered, keyed collection. (Or, possibly, to specify the order value for an object used to represent a keyed, ordered collection.)

I guess you could just do without the header and specify the metadata using magic values (in the same way null is used as a special value meaning delete), but it seems better to opt in to things things like that.

(IMO, json merge patch would have been slightly better if it had no special values by default, but it's not bad. "null means delete" is a small thing, you probably need delete regardless, and, anyway, the ship has sailed on that one.)

20. account42 ◴[] No.41904589{3}[source]
Unless your array is only a relatively small part of the file this defeats the purpose of patching. And yes, concurrency is also an issue in simple cases (e.g. append/prepend) that json patch handles fine on its own.