Network Working Group J.M. Snell
Internet-Draft January 07, 2013
Intended status: Informational
Expires: July 11, 2013

The application/json-merge-patch Media Type
draft-snell-merge-patch-08

Abstract

This specification defines the application/json-merge-patch media type and it's intended use with the HTTP PATCH method defined by RFC 5789.

Status of This Memo

This Internet-Draft is submitted to IETF in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet- Drafts is at http:/⁠/⁠datatracker.ietf.org/⁠drafts/⁠current/⁠.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on July 11, 2013.

Copyright Notice

Copyright (c) 2013 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http:/⁠/⁠trustee.ietf.org/⁠license-⁠info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document.


Table of Contents

1. Introduction

This specification defines the JSON "Merge Patch" document format, processing rules, and associated MIME media type identifier. The Merge Patch format is primarily intended for use with the HTTP PATCH method [RFC5789] as a means of describing a set of modifications to a subset of target resource's content.

For example, given the following original JSON document:

  {
    "a": "b",
    "c": {
      "d": "e"
    }
  }
      

A change to the value of the "a" member can be described simply as:

  PATCH /target HTTP/1.1
  Host: example.org
  Content-Type: application/json-merge-patch
  
  {"a": "z"}
      

When applied to the target resource, only the value of the "a" member will be modified, leaving the remaining content untouched.

The Merge Patch format generally supports two types of changes: removing and setting JSON object members. JSON arrays are treated the same as JSON primitives: the whole value can be replaced, but not partially modified. The JSON null value is given a special meaning to indicate the removal of an existing value. These constraints allow Merge Patch to use a format that closely mimics the document being modified. The constraints mean Merge Patch is suitable for patching JSON documents that primarily use objects for their structure, and do not make use of explicit null values. The Merge Patch format is not appropriate for all JSON syntaxes.

In this document, the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in [RFC2119].

2. The "application/json-merge-patch" Media Type

The "application/json-merge-patch" Media Type is used to identify JSON documents that describe, by example, a set of changes that are to be made to a target resource. When used within an HTTP PATCH request, it is the responsibility of the server receiving and processing the request to inspect the payload entity and determine the specific set of operations that are to be performed to modify the target resource.

For example, given the following example JSON document:

  {
    "title": "Goodbye!",
    "author" : {
      "givenName" : "John",
      "familyName" : "Doe"
    },
    "tags":["example","sample"],
    "content": "This will be unchanged"
  }
      

If the intent is to change the value of the "title" member to from "Goodbye!" to the value "Hello!", add a new "phoneNumber" member, remove the "familyName" member from the "author" object, and remove the word sample from the "tags" Array, the user-agent would send the following request:

  PATCH /my/resource HTTP/1.1
  Host: example.org
  Content-Type: application/json-merge-patch; charset="UTF-8"
  
  {
    "title": "Hello!",
    "phoneNumber": "+01-123-456-7890",
    "author": {
      "familyName": null
    },
    "tags": ["example"]
  }
      

Upon receiving the request, the server is responsible for inspecting the payload and determining, based on it's own understanding of the target resource media type and the underlying data model of the target resource, what specific operations will be applied to modify the resource.

A server receiving this patch request MUST apply the following rules to determine the specific set of change operations to be performed:

  1. If either the root of the JSON data provided in the payload or the root of the target resource are JSON Arrays, the target resource is to be replaced, in whole, by the provided data. Any object member contained in the provided data whose value is explicitly null is to be treated as if the member was undefined.
  2. If the root of the JSON data provided in the payload is an Object, for each distinct member specified within that object:

Applying these rules to the previous example, the set of specific change operations derived from the request are:

The resulting JSON document would be similar to the following (the specific ordering of members within JSON documents is insigificant):

      {
        "title": "Hello!",
        "author" : {
          "givenName" : "John"
        },
        "tags":["example"],
        "content": "This will be unchanged",
        "phoneNumber": "+01-123-456-7890"
      }
      

Once the set of intended modifications is derived from the request, the server is free to determine the appropriateness of the modification based on it's own understanding of the target resource. For instance, in the previous example, it is possible that the "familyName" member might be required within the target resource and cannot be removed. Note that in such cases, per [RFC5789], Section 2, the server is REQUIRED to reject the entire PATCH request using an HTTP error response code appropriate to the error condition.

If the request attempts to remove a member from the target resource that does not currently exist, the server SHOULD NOT consider the request to be in error. The requested removal operation is simply to be ignored by the server as the final modified state of the target resource will still accurately reflect the user-agent's original intent.

3. IANA Considerations

This specification registers the following additional MIME Media Types:

4. Security Considerations

The "application/json-merge-patch" Media Type allows user agents to indicate their intention that the server determine the specific set of change operations to be applied to a target resource. As such, it is the server's responsibility to determine the appropriateness of any given change as well as the user agent's authorization to request such changes. How such determinations are made is considered out of the scope of this specification.

All of the the security considerations discussed in Section 5 [RFC5789] apply to all uses of the HTTP PATCH method with the "application/json-merge-patch" Media Type.

5. Normative References

[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC5789] Dusseault, L. and J. Snell, "PATCH Method for HTTP", RFC 5789, March 2010.
[RFC4627] Crockford, D., "The application/json Media Type for JavaScript Object Notation (JSON)", RFC 4627, July 2006.

Appendix A. Example JavaScript Implementation

The following example implementation is provided as is, without warranty. It is provided in the public domain.

  // Apply the patch to the original, return the 
  // modified object... this will mutate the 
  // passed in object in place as well...
  function apply(orig, patch) {
    if (patch == null) 
      return orig;
    else if (patch instanceof Array)
      orig = purge_nulls(patch);
    else if (is_primitive(patch))
      orig = patch;
    else if (patch instanceof Object) {
      for (m in patch) {
        if (orig.hasOwnProperty(m)) {
          if (patch[m] == null)
            delete orig[m];
          else {
            if (is_primitive(patch[m])) 
              orig[m] = patch[m];
            else {
              if (orig[m] instanceof Array)
                orig[m] = purge_nulls(patch[m]);
              else 
                orig[m] = apply(orig[m],patch[m]);
            }
          }
        } else if (patch[m] != null)
          orig[m] = purge_nulls(patch[m]);
      }
    }
    return orig;
  }


  function is_primitive(val) {
    var m = typeof val;
    return m == 'string'  || 
           m == 'number'  ||
           m == 'boolean';
  }

  function purge_nulls(obj) {
    for (m in obj) {
      if (obj[m] == null) {
        if (obj instanceof Array)
          obj.splice(m,1);
        else
          delete(obj[m]);
      } else if (obj[m] instanceof Object)
        purge_nulls(obj[m]);
    }  
    return obj;
  }

  // Define the original object...
  var orig = {
    "a": "b",
    "c": {
      "d": [1,2,3],
      "e": {
        "f": 1
      }
    }
  }

  // Define the patch...
  var patch = {
    "c": {
      "d": [1,2],
      "e": {
        "f": null
      }
    }
  }

  // Apply the patch...
  var modified = apply(orig,patch);
      

Author's Address

James M Snell EMail: jasnell@gmail.com