td-patch

0.2.1 • Public • Published

td-patch

A JSON Patch module for Node.js, the browser, and the command line.

Install it with

npm install td-patch -g

or download the minimized version to be used in a browser.


For XML there are XSLT and XQuery for manipulating documents, and XPath for pointing to nodes in a document. What is the corresponding solution for JSON? It is JSON Path and JSON Pointer. Both XML and JSON are data interchange formats, but they are quite different. XML is more complex with attributes, namespaces, comments, and schemas. It needs complex tools. JSON is very close to the native data structure of many programming languages, JavaScript in particular. The natural way is to handle data manipulation in a programming language of choice, but there are situations where there is a need to write down a transformation in a standardized way, so it can be applied to a JSON later on. This is mostly what XSLT and XQuery is about, making templates for transformation of documents. JSON Patch is exactly that for JSON.

JSON Pointer

The first thing we need is to have a way to point at a node in a JSON structure, an address string. JSON Pointer is the simplest possible solution to that.

var json = {
    "module": "td-path",
    "version": "0.1.0",
    "keywords": [
     "tripledollar",
     "json-path",
     "json-pointer"
   ],
   "bin": {
   "tdpath": "bin/tdpath.js"
   },
   "author": "steenk",
   "license": "MIT"
},
 
    pointer1 = "/",
    pointer2 = "/keywords/1",
    pointer3 = "/version",
    pointer4 = "/bin/tdpath"

A JSON structure consists of a tree of arrays and objects with key/value pairs. To access a key/value pair the key is used, and to access any item in an array an index number is used. JSON Pointer use a combination of these keys and index numbers with slashes dividing them. It looks like an Internet address, which is also the meaning.

So what if a key has a "/" inside it? To prevent the key from being split into two invalid keys, it has to be escaped. The escape character used in JSON Pointer is "~". So the "/" in the key name is replaced by "~1" in the JSON Pointer string. That leaves the question of what if the key has a "~" in it? Well then it has to be replaced by "~0". These cases are not so common and all other valid characters in a JSON key goes without change.

JSON Patch

While JSON Pointer gives us a way to point to the inner data structures of a JSON document, JSON Patch gives us a way to describe transformations of a JSON document. JSON Patch is itself a JSON document. It always consists of an array with one or many patches. Each patch will do one of six operations on the JSON document, test, remove, add, replace, move, and copy. The patches will be applied in sequence, so it is actually an activity list for a transformation. The transformation is atomic, meaning that if any of the operation steps fails, the whole transformation fails. Five of the six operations will try to transform the JSON in some way, but the test operation just do a validation, which if it fails stops the whole transformation. Here is an example. It starts with the object {"b": "bar", "c": [1, 2, 3, 4], "d": {"e": {"f": {} } } } and transform it with a sequence of patches.

var res = tdpatch(
    {"b": "bar", "c": [1, 2, 3, 4], "d": {"e": {"f": {} } } }, 
    [
      {"op": "add", "path": "/a", "value": "foo"},
      {"op": "test",  "path": "/a",  "value": "foo"},
      {"op": "remove",  "path": "/b"},
      {"op": "remove",  "path": "/c/2"},
      {"op": "replace",  "path": "/a",  "value": "bar"},
      {"op": "replace",  "path": "/d/e/f",  "value": "foobar"}
    ]
);
 
// res: { "c": [ 1, 2, 4 ], d: { e: { f: 'foobar' } }, a: 'bar' }

All patches has the properties "op" and "path", add, test, and replace have the "value" property also, and move and copy have the "from" property also. In res comes the transformed object { c: [ 1, 2, 4 ], d: { e: { f: 'foobar' } }, a: 'bar' } if it succeeds, otherwise res will be undefined.

Chain Transformation

An alternative is to build up the transformation with chaining. If only the the first parameter is put into tdpatch, the patches can be applied with methods chained together and finally run by the run method. In the parameter list of these methods, the first one is always the path.

var obj = {"b": "bar", "c": [1, 2, 3, 4], "d": {"e": {"f": {} } } };
 
// start at transformation
var trans = tdpatch(obj)
    .add("/a", "doggy");
 
// add a test
trans.test("/a",  "doggy");
 
// chain the rest and run
var res = trans.remove("/b")
    .remove("/c/2")
    .replace("/a",  "billi")
    .replace("/d/e/f",  "foobar")
    .run();
 
console.log(res);

With this method you can build up a long patchlist, and whenever you want to export your patches do it with JSON.stringify(trans.patches), and you get a JSON string you can save for later use.

How to Get the Library

In Node.js this is what to do:

var tdpatch = require('td-patch');
 
var res = tdpatch({}, [{op: 'add', path: '/foo', value: 'bar'}]);
// res: {foo: 'bar'}

In the browser the prefered way is to use an AMD module loader, like Require.js, but without a module loader it will register a global method window.tdpatch.

require(['td-patch'], function (tdpatch) {
    
    var tdpatch = require('td-patch');
    var res = tdpatch({}, [{op: 'add', path: '/foo', value: 'bar'}]);
    // res: {foo: 'bar'}
});

The Command Tool

Installed in Node.js with the "-g" flag gives a command tool called "tdpatch".

tdpatch --help
Usage: tdpatch <json file> <json patch file> [<output file>]

The JSON Patch file has an array with one or more patches in it. It has to be in valid JSON format. The output file is optional.

Readme

Keywords

Package Sidebar

Install

npm i td-patch

Weekly Downloads

1

Version

0.2.1

License

MIT

Last publish

Collaborators

  • steenk