JSON Operator
Perform efficient path based operations on JSON Objects (or most Javascript data object).
Uses jsonpath with new delete
option for apply
.
Install
npm i json-operator --save
API
Note: The arguments indent
, path
and opts
are always optional.
Constructor
constructor(target, {path, jsonpath, mergeFun, createMerge})
Setters and getters
.path
: default operator path.target
: target object.jp
: jsonpath engine.createMerge
:function(opts)
createMerge
can be set to factory function which returns custom merge
function used by merge
if present. The opts
will be the options passed to merge
enriched with targetObj
and mergeObj
Getters
.history
: history list ofpath
andoperation
made.result
: modified target object.lastPath
: last path mutation applied
Display
targetAsStr(indent = 2)
: get prettified string of target objdisplay(indent, logger)
: display prettified string of target obj using logger (defaultconsole.log
)
Read values
query(path)
: get all match results in listvalue(path)
: get first match resultparent(path)
: parent of first match
Path operation scope*
with(path)
: setwithPath
used in new operations scopewithSame()
: uselastPath
aswithPath
in new operations scopeand()
: alias towithSame
done()
: exit current operation scope
Mutations
opts
is an optional argument which may contain:
path
: to override current scope pathcondition
: function to be called with nodevalue
andcontext
(ie.parent
andkey
) to decide if mutation/action should be performed.
This condition
function has the signature:
function(value, {parent, key}) : boolean
Insert
concat(insertObjs, opts = {})
: concatenate object(s) at the end of parent Arrayprepend(insertObj, opts = {})
: prepend object before matching nodeappend(insertObj, opts = {})
: append object after matching nodeinsertAt(insertObj, key)
: insert object at key on target object
Delete
delete(opts = {})
: delete matchessplice(opts = {})
:splice
on target object (start
,deleteCount
,insertObj
options)
Set
overwrite(obj, opts = {})
: set match to new object
Merge
merge(obj, opts = {})
: merge matches with new objectdeepMerge(obj, opts = {})
: deep merge matches with new objectreverseMerge(obj, opts = {})
: merge matches with new object
Delegation (general purpose)
apply(fn, path)
: delegates tojsonpath.apply
filter(fn, path)
: delegates tojsonpath.filter
Flatten/Unflatten
Use the flatten
and unflatten methods. First you must set the flattener to use, such as flat
or soon to be flat2
(WIP)
Normalize
Use the normalize
method which uses normalizr
For flatten
and normalize
usage examples, see the Changelog.md
or the documentation for each module (flat
and normalizr
).
All mutators can be chained beautifully :)
let finalResult = operator // use latest path for new scope // overwrite using latest scope path // merge using custom path // use latest path for new scope // reusing latest path // create scope with path // merge using scope path // create inner scope path // merge using current scope path // close current scope result;
You can add your custom functions directly to the operator
object or the class JsonOperator.prototype
.
Usage
Given the following book store:
"store": "book": "category": "reference" "author": "Nigel Rees" "title": "Sayings of the Century" "price": 895 "category": "fiction" "author": "Evelyn Waugh" "title": "Sword of Honour" "price": 1299 "category": "fiction" "author": "Herman Melville" "title": "Moby Dick" "isbn": "0-553-21311-3" "price": 899 "category": "fiction" "author": "J. R. R. Tolkien" "title": "The Lord of the Rings" "isbn": "0-395-19395-8" "price": 2299 "bicycle": "color": "red" "price": 1995
Note: Books are indexed from 0 as normal, so book [2] is the 3rd book in the list.
We perform the following operations to:
- get the value of book [2]
- get the value of book [3]
- set the default opertor path to book [3]
- overwrite book 3 with book [2]
- merge
{price:100}
onto book [3] - reverse order merge
{rating: 4}
onto book [3] - delete book [3]
- display full target object after operations
Full API example
const store = const operator = store let paths = book2: '$..book[2]' book3: '$..book[3]' // query for all matching pathlet book2results = operator;console // get the first match for book 2let book2val = operatorvaluepathsbook2;console // let book2par = operator.parent(paths.book2);// console.log('original book 2 parent', book2par) // get the first match for book 3 let book3 = operatorvaluepathsbook3;console // set default path to use for the following opsoperatorpath = pathsbook3; // overwrite book3 with book2operatorlet book3Set = operatorvalue; consoleconsole // overwrite book2 with value found at book3operatorlet book3Merged = operatorvalue consoleconsoleconsole // operator.merge({price: 100}, {reverse: true})operatoroperatoroperator operatorlet book3deleted = operatorvalueconsoleconsole
Custom merge
You can define a custom merge function as follows:
// return merge function dependent on option .admin settingoperator { return optsadmin ? fullMerge : partialMerge; } // do full mergeoperator // return merge function dependent on whether role of target object (a User) is 'admin'operator { return optstargetObjrole === 'admin' ? fullMerge : partialMerge; } operatortarget = name: 'kris' role: 'admin' // do full mergeoperator; operatortarget = name: 'sandy' role: 'guest' // do partial mergeoperator;
The example demo (excluding use of createMerge
) can be found in /examples/demo.js
in the repo.
Set alternative jsonpath engine
You can now also set an alternative jsonpath
engine.
const fastpath = ;operatorjp = fastpath;
Note: Some jsonpath
alternatives don't yet have a filter
function (for delete
) and be fully compatible. You might however be able to add filter
(and apply
) from jsonpath?
fastpathfilter = jsonpathprototypefilter;
Advanced usage
Example:
var all = fs
To operate efficiently on a folder with json
file perhaps use readdirp to read directory as a stream.
See next example for inspiration.
Mongoose streaming example
const paths = // ... userRole: '$.user.role'; const jsonTransformStream = paths ; Model;
const Transform = require('stream').Transform
module.exports = class JsonTransformStream extends Transform {
constructor(options = {}) {
this.operator = new JsonOperator();
this.paths = options.paths;
this.objs = options.objs;
}
// do some user role operations!
operateOn(obj) {
this.operator.target = chunk;
if (obj.user) {
this.operator.merge(this.objs.role, {path: this.paths.userRole})
}
this.push(this.operator.result);
}
_transform(chunk, encoding, done) {
this.operateOn(chunk)
done();
}
_flush(done) {
done();
}
}
Alternatives
Some alternative jsonpath
transformers
- jsonpath-plus
- jsonpath-transform
- jsonpath-object-transform
- jsonpath-rep with replacement
Would be nice if we could combine the best of all ;)
JSON path alternative
Misc
Licences
MIT
Kristian Mandrup kmandrup@gmail.com 2016