PATCH: It is the Diff
Prompted by Joe Gregorio’s How To Do RESTful Partial Updates, there were a lot of discussions over the weekend about partial updates. James Snell just captured the list of reactions in his latest post, and so there is no need to repeat them here. While Joe Gregorio’s post is an interesting attempt at encoding the diff into URIs, I would argue that, contrary to what the title of his post suggests, encoding a diff into a URIs is not a generic solution for partial updates. Please note that, while Joe Gregorio’s post just focused on Atom, I am extending this to XML in general in this post.
The key issue with PATCH is describing a partial update to a resource. True, there are no generally accepted diff formats for various content-types, particularly for JSON and XML. But, describing diff in URIs is a bad idea.
Considering an XML representation of a resource, a partial update can include any number of components from the XML infoset. Even if we just focus on elements and attributes, the diff format needs to consider adding, removing or updating elements and attributes. Take, for example, Joe Gregorio’s example:
http://example.org/edit/first-post/X1;X3
In his scheme, such a URI means that the client is updating two elements with xml:id attributes of values X1 and X3.
If a client wants to delete an element with xml:id attribute of value X2 the client would use a URI
http://example.org/edit/first-post/X1;X2;X3
but exclude the element being deleted from the body of the POST request. So far so good. How about attributes? No easy answer in this scheme!
Let me take another example.
<person> <first-name>Subbu</first-name> <last-name>Allamaraju</last-name> <addresses> <address> <type>home</type> <street>1 Main Street</street> <city>Some City</city> </address> </addresses> </person>
How would a client describe the following partial update in a URI?
- Add middle name
- Change the address type to "work"
- Add zip code to the address.
Here is a possibility
http://example.org/persons/subbu;a1;a2;a3
with the request body described as
<person> <middle-name xml:id="a1">V</middle-name> <addresses> <address> <type xml:id="a2">work</type> <zip xml:id="a3">12345</zip> </address> </addresses> </person>
To allow such an XML document, the server developer will have to weaken the schema. For instance, if first-name and last-name elements were also supposed to be there for other GETs, PUTs, and POSTs, they will be optional for a partial update. In fact, to support partial updates, the schema will need to make most of the elements and attributes optional.
For the developer creating this partial update request, there are two tasks at hand:
- Carefully construct a partial update document
- Carefully construct a URI describing the diffs following the scheme described by Joe Gregorio
IMO, the second step is unnecessary, and introduces extra complexity while not simplifying the first step. The first step is, anyway, necessary to describe the partial update. But if we follow Joe Gregorio’s scheme, the first step would be incomplete as it only identifies the elements that are being updated or deleted. It is the upto the URI to describe the kinds of changes. This could very well be modeled as the following PATCH request:
PATCH /persons/subbu Content-Type: application/xml-diff
<person xmlns:diff="http://www.example.com/diff"> <middle-name diff:type="insert">V</middle-name> <addresses> <address> <type diff:type="update">work</type> <zip diff:type="insert">12345</zip> </address> </addresses> </person>
This model is much more extensible than the one described by Joe Gregorio and leaves the URI to do what it is supposed to - i.e. identify the resource. The body is described a content-type that is meant to describe partial updates to XML documents. Such a format can be designed to consider not just elements, but the rest of the XML infoset as well. While there is no standard way to describe a diff for the XML infoset, there were attempts. See for example, the .NET XML Diff and Patch Tool. Using such a scheme avoids the need to design weak schemas to describe partial updates, and the above request could be rewritten as follows:
PATCH /persons/subbu Content-Type: application/xml-diff
<xd:xmldiff version="1.0" srcDocHash="18037794330406883885" options="None" fragments="no" xmlns:xd="http://schemas.microsoft.com/xmltools/2002/xmldiff"> <xd:node match="1"> <xd:node match="1"/> <xd:add> <middle-name>V</middle-name> </xd:add> <xd:node match="3"> <xd:node match="1"> <xd:node match="1"> <xd:change match="1">work</xd:change> </xd:node> <xd:node match="3"/> <xd:add> <zip>12345</zip> </xd:add> </xd:node> </xd:node> </xd:node> </xd:xmldiff>
So, once we remove the URI clumsiness, we end up with a PATCH format that, when applied to the resource, partially updates it. As Aristotle Pagaltzis remarks,
… first, give this patch document format a MIME type. Then define a t:delete element to put under the t:partial document element, where it can appear any number of times, and whose content is the t:edit-id of an element to be deleted. Thus, the URI of the synthetic partial resource becomes unnecessary to interpret the patch document correctly. The bottom line is that you can stuff such a document into a PATCH request to the Atompub edit URI, obviating the URI construction gymnastics in entirety.
There it is. Once you fix all the open issues that Joe Gregorio mentions in his post, what we end up with is a PATCH.



No comments yet.