json-patch / json-patch2

A possibile revision to the JSON-Patch format
44 stars 0 forks source link

New JSON Patch Operators and Modifiers #14

Open huggsboson opened 8 years ago

huggsboson commented 8 years ago

We've loved using the JSONPatch specification thus far and have built a number of internal and external APIs to use it. I wanted to get your thoughts on a few additions we will be pursuing to the JSONPatch spec (using a different content-type application/vnd.box.json-patch+json).

You are already aware of our desire for a strict-add operator and put it on the json-patch2 issue list. We are going to use patches to help us describe migrations on json which have a number of optional fields. We ended up wanting to make sure that everything made sense and was consistent with the existing set of operators and easy to reason about. Also we are contemplating a few new operators for dealing with paths with certain values.

Before I jump in, I realize this ups the complexity of the spec a bit, but we actually have need for each and everyone of these new operators and new modifiers. Some of the combinations of operators and modifiers aren't really needed for us, but we wanted to specify them fully to ensure that the convention we described actually generalizes properly.

Modifiers

The rule we've found when studying the existing operators is: Operators throw if source does not exist and do not care if dest exists or does not exist

What we are looking to do is add a number of new operators that follow this naming convention: A suffix on operators for modifiers on source: ? = skip if source does not exist

A suffix on operators for modifiers on dest: ! = throws if dest exists

Here's a helpful model for understanding:

screen shot 2015-08-31 at 3 37 56 pm

​ add: { "op": "add", "path": "/foo", "value": "bar" } -- destination only add -- overwrite dest either way add! -- throw if dest is exists (aka the-one-true-add) add* -- skip if dest exists (aka default operator)

remove: { "op": "remove", "path": "/foo" } -- source only remove -- throw if source is missing otherwise delete remove? -- skip if source is missing

replace: { "op": "replace", "path": "/foo", "value": "bar" } -- source only replace -- throw if source is missing otherwise replace value replace? -- skip if source is missing

copy: { "op": "copy", "from": "/foo", "path": "/bar" } copy -- throws if source does not exist otherwise set or overwrite copy? -- skips if source does not exist copy! -- throws if dest already exists copy* -- skips if dest already exists ban: copy?! -- throw if dest exists or skip if source is missing ban: copy?* -- skip if source does not exist or dest does exist

move: { "op": "move", "from": "/foo", "path": "/bar" } move -- throws if source does not exist move? -- skips if source does not exist move! -- throws if source already exists, throws if dest already exists move* -- skip if dest already exists ...

test: { "op": "test", "path": "/foo", "value": "bar" } -- source only test -- throws if does not exist or is not equal value test? - skips if source does not exist !test -- throws if does not exist or value is equal, special. !test? -- skip if source does not exist or throw if value is equal, special.

New Operators

We are also looking to add some new base operators: exists, translate, omit. exists: { "op": "exists", "path": "/foo" } -- source only exists -- throws if source does not exist !exists -- throws if source exists, special.

translate: { "op": "translate", "path": "/foo", "from": "bar", "value": "baz" } -- source only translate -- translates values at path that match "from" to "value" translate? -- skip if source does not exist

omit: { "op": "omit", "path": "/foo", "value": "bar" } -- source only omit -- remove the property or item if it matches value, throw if source does not exist omit? -- skip if source does not exist

So yeah, I know you are pretty open to extensions as long as we are clear about the content-type. I'd just love any feedback you have about the design choices we've made so we can avoid deviating too much from any future plans you have.

konrad7d commented 7 years ago

+1 especially for the ability to test for element non-existence (the "!exists" keyword).

mitar commented 4 years ago

I think this is adding to much complexity to JSON patch. I think JSON patch should just provide a diff against a known version of base JSON and then external algorithms should be used to fuzzy-apply this if base JSON does not exactly match. I would suggest that JSON patch only standardizes some ways to provide more operation-level and global-level context to help with such fuzzy-apply algorithms.

The problem is that JSON is an array, Maybe we should make it an object with one property for steps of the patch, so:

{
  "context": <value>,
  "steps": [
    ...
  ]
}

This would make it much easier to extend. And add some global metadata to the whole set of steps. While additional metadata could go into each step as well.