Re: [Json] JSON merge alternatives
Mark Nottingham <mnot@mnot.net> Wed, 19 March 2014 23:57 UTC
Return-Path: <mnot@mnot.net>
X-Original-To: json@ietfa.amsl.com
Delivered-To: json@ietfa.amsl.com
Received: from localhost (ietfa.amsl.com [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id CB7151A0817 for <json@ietfa.amsl.com>; Wed, 19 Mar 2014 16:57:15 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -1.902
X-Spam-Level:
X-Spam-Status: No, score=-1.902 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001] autolearn=ham
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id YKg72hoRil76 for <json@ietfa.amsl.com>; Wed, 19 Mar 2014 16:57:12 -0700 (PDT)
Received: from mxout-07.mxes.net (mxout-07.mxes.net [216.86.168.182]) by ietfa.amsl.com (Postfix) with ESMTP id 48BC71A0811 for <json@ietf.org>; Wed, 19 Mar 2014 16:57:12 -0700 (PDT)
Received: from [192.168.1.55] (unknown [118.209.54.174]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No client certificate requested) by smtp.mxes.net (Postfix) with ESMTPSA id AB72B22E200; Wed, 19 Mar 2014 19:56:55 -0400 (EDT)
Content-Type: text/plain; charset="us-ascii"
Mime-Version: 1.0 (Mac OS X Mail 7.2 \(1874\))
From: Mark Nottingham <mnot@mnot.net>
In-Reply-To: <20140319234549.GA3471@localhost>
Date: Thu, 20 Mar 2014 10:56:52 +1100
Content-Transfer-Encoding: quoted-printable
Message-Id: <8489D083-3871-4516-8FD3-32749645DBB4@mnot.net>
References: <20140319234549.GA3471@localhost>
To: Nico Williams <nico@cryptonector.com>
X-Mailer: Apple Mail (2.1874)
Archived-At: http://mailarchive.ietf.org/arch/msg/json/kO3XZJieNt12Zqe3sd1VzjorgiM
Cc: json@ietf.org
Subject: Re: [Json] JSON merge alternatives
X-BeenThere: json@ietf.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id: "JavaScript Object Notation \(JSON\) WG mailing list" <json.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/json>, <mailto:json-request@ietf.org?subject=unsubscribe>
List-Archive: <http://www.ietf.org/mail-archive/web/json/>
List-Post: <mailto:json@ietf.org>
List-Help: <mailto:json-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/json>, <mailto:json-request@ietf.org?subject=subscribe>
X-List-Received-Date: Wed, 19 Mar 2014 23:57:16 -0000
Hi Nico, http://mailarchive.ietf.org/arch/msg/apps-discuss/mvUVvUt41tFTrPSOCfv7sfblTUM Cheers, On 20 Mar 2014, at 10:45 am, Nico Williams <nico@cryptonector.com> wrote: > First, IMO the WG should in fact adopt a WG item (or more) for MIME > types for use with PATCH to patch JSON texts (or entities with JSON > representations). This is commonly known as "JSON merge", IIUC. > > I work with one Rails application that has an ad-hoc JSON merge like > schema for representing updates (PUTs) to resources -- this schema is > very specific to the application in question, therefore not reusable. > > I can imagine a Ruby gem that handles a JSON merge PATCH in a completely > general and reusable way. And not just Ruby, of course. > > Second, I believe the JSON merge proposals I've seen to date are no > good. > > The goal of any Internet JSON merge schema should be to: > > Concisely and generally express "edits" to a JSON text, efficiently > allowing: > > - replacement of any values > - prepend/insert/replace/append to arrays > - addition/removal of names from objects > - setting null values to object names > > no matter how deeply (subject to a max) or shallowly nested these > values be in the original, while being easy to produce and apply. > > Note: it helps to use If-Match to make sure that the resource being > PATCHed has not been modified since it was fetched. This allows one to > express edits with confidence. > > I have two proposals: one based on augmenting a subset of the original > JSON text, the other based a sequence of paths and edit instructions for > the values found at those paths. > > > Proposal #1: Patch as augmented subset of the original > > Follow these steps to produce a patch: > > - Start with the resource's JSON representation. > > - Remove all interior object name/value pairs that are not on the way > to any value to be added/replaced/removed/otherwise edited. > > - Remove all interior array elements that are not on the way to any > value to be added/replaced/removed UNLESS the element is in an array > to be edited (more on this below). > > - Insert before each remaining array element its index. > > - To replace any value with a scalar, just write the new value where > the old one appeared. > > - To append to any array, just append -1 and the new value to it (after > the above removals and additions). > > - For any array you want to do more to than append -> wrap the array in > an object with any of these named values: > > o "delete": [<list of array indices>] > o "prepend": [<list of values to append>] > o "insert": [<list of [<index>, <value>]>] > o "set": [<list of [<index>, <value>]>] > o "new": [<list of new elements to replace the old one's with>] > o "edits": [<list of edits (see below)>] > > To disambiguate these objects from objects that could legitimately > appear in the resource the following named value MUST also be > present: > > o "<URN TBD>": true > > Replacements to be performed first, then deletions, then insertions, > then prepends, then "edits". Edits are what would have appeared > had there been no need to wrap the array in order to do delete, > prepend, replace, or insert elements. > > - To add name/value pairs to objects, just add them. > > - If you want to delete values from objects -> wrap the object in an > object with any of these names: > > o "delete": [<list of name strings>] > o "edits": <object containint edits> > > Edits are what would have appeared had there been no need to wrap the > object to delete name/value pairs. > > To disambiguate these names the following named value MUST also be > present: > > o "<URN TBD>": true > > Values to be added/set appear as they should appear in the resource as > patched. > > Patch application is straightforward, obvious. > > Example resource: > > { > "a": [ { "b": 1, "c": 2 }, { "d": { "e": [ 2, {} ] } } ], > "z": [ true, false, null, null, "hello" ] > } > > Example edits: > > - Delete that empty object (path a[0].d.e[1] in jq-speak) > > { "a": [ 1, { "d": { "e": { "delete": [ 1 ], > "urn:ietf.org:TBD": true } } } ] } > > - Replace the array [ 2, {} ] (path a[0].d.e in jq-speak) with the > value 0: > > { "a": [ 1, { "d": { "e": 0 } } ] } > > - Append true to the array [ 2, {} ] (path a[0].d.e in jq-speak): > > { "a": [ 1, { "d": { "e": [ true ] } } ] } > > - Delete the 1, prepend "foo", append "bar" to that same array: > > { "a": [ 1, { "d": { "e": { "delete": [ 0 ], > "prepend": [ "foo" ], > "edits": [ "bar" ], > "urn:ietf.org:TBD": true } } } ] } > > - Add a sibling name to "b" and "c": > > { "a": [ 0, { "f": "hello" } ] } > > - Delete "b": > > { "a": [ 0, { "delete": [ "b" ], "urn:ietf.org:TBD": true } ] } > > - Replace the "z" array with [1, 2, 3]: > > { "z": { "new": [ 1, 2, 3, ], "urn:ietf.org:TBD": true } } > > Proposal #2: Use sequence of [<path>, <edit instruction>] > > - Write the path to each value to be edited as an array of path > elements (strings for object names, numbers for array elements). > > - To replace any value with a scalar value (non-array, non-object), > just write the path to the value and the new value as the > instruction. > > - To add a value to an object just write the path to the object, append > the new value's name to the path, and the instruction will be the new > value. > > - To add (append) a value to an array, write the path to the array, > append a -1 to the path, and the instruction will be the new value. > > - Edit instructions for arrays (other than appending to, or replacing > the whole array with a scalar value) are any objects like: > > o "delete": [<list of indices>] or true (if path names an array > element) > > o "add": [<list of <value>s to append] > > o "insert": [<list of [<index>, <value>] pairs>] > o "set": [<list of [<index>, <value>] pairs>] > o "prepend": [<list of <value>s to prepend] > o "add": [<list of <value>s to append] > o "new": [<new list of values to replace old ones>] > > No magic URN name is needed in this case. > > - To add an object > > - Edit instructions for objects (other than replacement with a scalar > value) are objects with name/value pairs like: > > o "delete": [<list of name strings>] > o "merge": [<object whose named value pairs will replace the > corresponding ones of the object being edited>] > > No magic URN name is needed in this case. > > The totality of the patch, then, is an array of > [ <path>, <instruction> ] elements. > > Any given path could be referenced multiple times in one patch. > > Proposal #2 is pretty self-explanatory. Examples left as an exercise > for the reader. > > Nico > -- > > _______________________________________________ > json mailing list > json@ietf.org > https://www.ietf.org/mailman/listinfo/json -- Mark Nottingham http://www.mnot.net/
- [Json] JSON merge alternatives Nico Williams
- Re: [Json] JSON merge alternatives Mark Nottingham
- Re: [Json] JSON merge alternatives Nico Williams
- Re: [Json] JSON merge alternatives Nico Williams
- Re: [Json] JSON merge alternatives Mark Nottingham
- Re: [Json] JSON merge alternatives Nico Williams
- Re: [Json] JSON merge alternatives Mark Nottingham
- Re: [Json] JSON merge alternatives Manger, James
- Re: [Json] JSON merge alternatives Nico Williams
- Re: [Json] JSON merge alternatives Manger, James
- Re: [Json] JSON merge alternatives Nico Williams
- Re: [Json] JSON merge alternatives Julian Reschke
- Re: [Json] JSON merge alternatives Stefan Drees
- Re: [Json] JSON merge alternatives Paul Hoffman