Repository files navigation

json-path-processor

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

npm versionnpm downloadDependency StatusBuild StatusTest CoverageCode ClimateLicense

Sauce Test Status

Installation

npm install json-path-processor

In browser:

<scriptsrc="dist/jpp.js"></script>

In an AMD loader:

require('jpp',function(jpp){/*....*/});

In nodejs:

varjpp=require('json-path-processor');

Features

Upgrade Notice

  • Now JPP stop using lodash and move to ES5. For none ES5 browsers/enviromnent, you should use polyfills to ensure JPP works well. You can try polyfill.io or polyfills.io.

  • Some lodash like method behavior changed, please refer to document.

Usage

varjpp=require('json-path-processor');// I wanna update all product titledata=jpp(data).each('product.title',function(V){returnsomething(V);}).value();// Ya, handle all product title and promotion description// almost all jpp methods are chainabledata=jpp(data).each('extra.promotion',function(O){O.description=someValue;returnO;}).each('product.title',someUtilFunc).value();// jpp play on the object reference so you even do not need to assign back!jpp(data).each('product',assignProductDefault);jpp(data).each('product',changeProductDetail);output(data);// chaining is cool, right?jpp(data).each('product',playTheProduct).each('product.title',fixProductTitle);output(data);

API document and example

  • jpp(data) : create the JPP chainning object by data.
varJ=jpp(['any','data',{or: {recursive: {'object'}}}]);
  • jpp(data, path) : a shortcut of jpp(data).value(path)
console.log(jpp({a: {b: 'OK'}},'a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}},'a.c.d'));// will get undefined
  • .value(path) : get value by JSON path. This method can not be chainned. When path is undefined or '' or '$', get whole data.
console.log(jpp([1,3,5]).value());// will get [1, 3, 5]console.log(jpp({a: {b: 'OK'}}).value('a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}}).value('a.c.d'));// will get undefined
  • .get(path) : get new JPP object by JSON path. All chainned methods on this is different from root object.
console.log(jpp({a: {b: 'OK'}}).get('a').get('b').value());// will get 'OK'
  • .set(path, value, create) : set new value by JSON path. When value is a function, execute the function with first argument as old value. the return value of the callback function will be assigned. When create exists, create new object by the JSON path, and create will be used as default value to be assigned when the callback function throws exception.
// will get {a: {b: 'OK', c:[1, 3]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.1',3).value());// will get {a: {b: 'BAD', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b','BAD').value());// will get {a: {b: 'OK', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?').value());// set failed ... WE CAN NOT CONVERT ARRAY TO OBJECTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?',true).value());// a.b.c[2 ~ 9] will become undefined ... ARRAY SIZE AUTO EXPEND IN JAVASCRIPTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.10','OK?',true).value());
  • .copy(from, to, skip) : copy value from one JSON path to another. When the JSON path not found, new object will be created. To prevent new object creation, pass skip as true as 3rd param.
// will get {a: {b: 'OK', c:[1, 4], d: 4}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).copy('a.c.1','a.d').value());
  • .del(path) : delete a key by JSON path. When the path exists, last key will be deleted; when it do not exist, do nothing.
// will get {a: {b: 'OK', c: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).del('a.b.c').value());// will get {a: {b: {}}console.log(jpp({a: {b: {c: {d: 2,q: 1}}}}).del('a.b.c').value());
  • .move(from, to) : move values from a path to another. when the origin path not found, do nothing.
// will get {a: {b: 'OK', d: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).move('a.c','a.d').value());
  • .range(path, args...) : Works like lodash.range() , create range and set the array into the path.
// will get {a: {b: 1, c: [0, 1, 2]}}console.log(jpp({a: {b: 1}}}).range('a.c',3).value());// will get {a: {b: 1, c: [3, 4]}}console.log(jpp({a: {b: 1}}}).range('a.c',3,5).value());// will get {a: {b: 1, c: [2, 5, 8, 11]}}console.log(jpp({a: {b: 1}}}).range('a.c',2,12,3).value());
  • .find(path, args...) : Works like lodash.find() , return the value. This method can not be chainned.
// will get 3console.log(jpp({a: {b: [0,3,4]}}}).find('a.b',function(O){returnO%2>0}));
  • .findLast(path, args...) : Works like lodash.findLast() , return the value. This method can not be chainned.
// will get 5console.log(jpp({a: {b: [1,3,4,5]}}}).find('a.b',function(O){returnO%2>0}));
  • .each(path, function (value, key) {...}) : Works like Array.map(), the callback arguments are: value, index. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not array. When your callback return undefined or throws, the item in array will not be changed.
console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V){returnV*2;}).value());// will get {a: {b: [2, 6, 10]}}console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V,I){// I as indexreturnV*I;}).value());// will get {a: {b: [0 , 3, 10]}}// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).each('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1 , 3, 5], c: 'ERROR'}}
  • .forIn(path, function (value, key) {...}) : Works like for (I in O), the callback arguments are: value, key. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not object. When your callback return undefined or throws, the item in object will not be changed.
// will get {a: 'OK!', b: 'BAD!', length: '9!'} // forIn() will not think object with length property as array.console.log(jpp({a: 'OK',b: 'BAD',length: 9}).forIn('$',function(V,I){returnV+'!';}).value());// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).forIn('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1, 3, 5], c: 'ERROR'}}
  • .filter(path, function (value, key) {...}) : Works like Array.filter() but also works well on object. the callback are: value, index|key. The filtered result will be assigned back to JPP object.
// will get {a: {b: {c: [3, 5], d:5}}}console.log(jpp({a: {b: {c: [2,3,4,5],d: 5}}}).filter('a.b.c',function(V){returnV%2>0;}).value());
  • .concat(path, path, path ...) : search for all values by proviced JSON path, then concat all arraies into one. None array value will be skipped. When array size >= 1, assign the concated array back to first path. Or, do nothing.
// will get {a: {b: {c: [1, 3], d:5}, d: [1, 3, 1,3]}}console.log(jpp({a: {b: {c: [1,3],d: 5}}}).concat('a.d','a.b.c','a.b.c').value());

Supported JSON Path

We only support absolute JSON Path and receive only one item.

  • $ : refer to self
  • $.foo.bar or .foo.bar or foo.bar or foo['bar'] or ['foo']['bar'] : refer to foo then bar
  • $.foo.3.bar or foo.3.bar or foo['3'].bar or foo['3']['bar'] : refer to foo then 4th item then bar

When you need to deal with . inside your property name, you can just use the array syntax:

// var result = data.sites['google.com'].pageRank.score;// prevent exception by jpp way:varresult=jpp(data).value("sites['google.com'].pageRank.score");

TODO handle \ escape inside the array syntax

The long story

All our life is to handle data....with a loop. Let's start from a basic loop:

for(Iindata){data[I]=something(data[I]);}

To make jslint happy or ensure the loop correct, we should add property check:

for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}

In real life, data is not always ready. We must handle none data case:

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}}

Furthermore, please catch something() because they may throw some error.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I]=something(data[I]);}catch(E){handle_error(E);}}}}

The more assign in the loop, the more try/catch you need.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I].title=something(data[I].title);}catch(E){handle_error(E);}try{data[I].desciption=something(data[I].description);}catch(E){handle_error(E);}try{data[I].url=something(data[I].url);}catch(E){handle_error(E);}}}}

The loop becomes a nightmare now, right? Let's use lodash to reduce indents in the loop:

_(data).each(function(V){try{V.title=something(V.title);}catch(E){handle_error(E);}try{V.desciption=something(V.description);}catch(E){handle_error(E);}try{V.url=something(V.url);}catch(E){handle_error(E);}});

But, lodash still can not reduce the try/catch hell for you. Now, JsonPathProcessor help on this!

About

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

Resources

Stars

26 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

json-path-processor

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

npm versionnpm downloadDependency StatusBuild StatusTest CoverageCode ClimateLicense

Sauce Test Status

Installation

npm install json-path-processor

In browser:

<scriptsrc="dist/jpp.js"></script>

In an AMD loader:

require('jpp',function(jpp){/*....*/});

In nodejs:

varjpp=require('json-path-processor');

Features

Upgrade Notice

  • Now JPP stop using lodash and move to ES5. For none ES5 browsers/enviromnent, you should use polyfills to ensure JPP works well. You can try polyfill.io or polyfills.io.

  • Some lodash like method behavior changed, please refer to document.

Usage

varjpp=require('json-path-processor');// I wanna update all product titledata=jpp(data).each('product.title',function(V){returnsomething(V);}).value();// Ya, handle all product title and promotion description// almost all jpp methods are chainabledata=jpp(data).each('extra.promotion',function(O){O.description=someValue;returnO;}).each('product.title',someUtilFunc).value();// jpp play on the object reference so you even do not need to assign back!jpp(data).each('product',assignProductDefault);jpp(data).each('product',changeProductDetail);output(data);// chaining is cool, right?jpp(data).each('product',playTheProduct).each('product.title',fixProductTitle);output(data);

API document and example

  • jpp(data) : create the JPP chainning object by data.
varJ=jpp(['any','data',{or: {recursive: {'object'}}}]);
  • jpp(data, path) : a shortcut of jpp(data).value(path)
console.log(jpp({a: {b: 'OK'}},'a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}},'a.c.d'));// will get undefined
  • .value(path) : get value by JSON path. This method can not be chainned. When path is undefined or '' or '$', get whole data.
console.log(jpp([1,3,5]).value());// will get [1, 3, 5]console.log(jpp({a: {b: 'OK'}}).value('a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}}).value('a.c.d'));// will get undefined
  • .get(path) : get new JPP object by JSON path. All chainned methods on this is different from root object.
console.log(jpp({a: {b: 'OK'}}).get('a').get('b').value());// will get 'OK'
  • .set(path, value, create) : set new value by JSON path. When value is a function, execute the function with first argument as old value. the return value of the callback function will be assigned. When create exists, create new object by the JSON path, and create will be used as default value to be assigned when the callback function throws exception.
// will get {a: {b: 'OK', c:[1, 3]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.1',3).value());// will get {a: {b: 'BAD', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b','BAD').value());// will get {a: {b: 'OK', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?').value());// set failed ... WE CAN NOT CONVERT ARRAY TO OBJECTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?',true).value());// a.b.c[2 ~ 9] will become undefined ... ARRAY SIZE AUTO EXPEND IN JAVASCRIPTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.10','OK?',true).value());
  • .copy(from, to, skip) : copy value from one JSON path to another. When the JSON path not found, new object will be created. To prevent new object creation, pass skip as true as 3rd param.
// will get {a: {b: 'OK', c:[1, 4], d: 4}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).copy('a.c.1','a.d').value());
  • .del(path) : delete a key by JSON path. When the path exists, last key will be deleted; when it do not exist, do nothing.
// will get {a: {b: 'OK', c: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).del('a.b.c').value());// will get {a: {b: {}}console.log(jpp({a: {b: {c: {d: 2,q: 1}}}}).del('a.b.c').value());
  • .move(from, to) : move values from a path to another. when the origin path not found, do nothing.
// will get {a: {b: 'OK', d: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).move('a.c','a.d').value());
  • .range(path, args...) : Works like lodash.range() , create range and set the array into the path.
// will get {a: {b: 1, c: [0, 1, 2]}}console.log(jpp({a: {b: 1}}}).range('a.c',3).value());// will get {a: {b: 1, c: [3, 4]}}console.log(jpp({a: {b: 1}}}).range('a.c',3,5).value());// will get {a: {b: 1, c: [2, 5, 8, 11]}}console.log(jpp({a: {b: 1}}}).range('a.c',2,12,3).value());
  • .find(path, args...) : Works like lodash.find() , return the value. This method can not be chainned.
// will get 3console.log(jpp({a: {b: [0,3,4]}}}).find('a.b',function(O){returnO%2>0}));
  • .findLast(path, args...) : Works like lodash.findLast() , return the value. This method can not be chainned.
// will get 5console.log(jpp({a: {b: [1,3,4,5]}}}).find('a.b',function(O){returnO%2>0}));
  • .each(path, function (value, key) {...}) : Works like Array.map(), the callback arguments are: value, index. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not array. When your callback return undefined or throws, the item in array will not be changed.
console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V){returnV*2;}).value());// will get {a: {b: [2, 6, 10]}}console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V,I){// I as indexreturnV*I;}).value());// will get {a: {b: [0 , 3, 10]}}// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).each('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1 , 3, 5], c: 'ERROR'}}
  • .forIn(path, function (value, key) {...}) : Works like for (I in O), the callback arguments are: value, key. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not object. When your callback return undefined or throws, the item in object will not be changed.
// will get {a: 'OK!', b: 'BAD!', length: '9!'} // forIn() will not think object with length property as array.console.log(jpp({a: 'OK',b: 'BAD',length: 9}).forIn('$',function(V,I){returnV+'!';}).value());// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).forIn('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1, 3, 5], c: 'ERROR'}}
  • .filter(path, function (value, key) {...}) : Works like Array.filter() but also works well on object. the callback are: value, index|key. The filtered result will be assigned back to JPP object.
// will get {a: {b: {c: [3, 5], d:5}}}console.log(jpp({a: {b: {c: [2,3,4,5],d: 5}}}).filter('a.b.c',function(V){returnV%2>0;}).value());
  • .concat(path, path, path ...) : search for all values by proviced JSON path, then concat all arraies into one. None array value will be skipped. When array size >= 1, assign the concated array back to first path. Or, do nothing.
// will get {a: {b: {c: [1, 3], d:5}, d: [1, 3, 1,3]}}console.log(jpp({a: {b: {c: [1,3],d: 5}}}).concat('a.d','a.b.c','a.b.c').value());

Supported JSON Path

We only support absolute JSON Path and receive only one item.

  • $ : refer to self
  • $.foo.bar or .foo.bar or foo.bar or foo['bar'] or ['foo']['bar'] : refer to foo then bar
  • $.foo.3.bar or foo.3.bar or foo['3'].bar or foo['3']['bar'] : refer to foo then 4th item then bar

When you need to deal with . inside your property name, you can just use the array syntax:

// var result = data.sites['google.com'].pageRank.score;// prevent exception by jpp way:varresult=jpp(data).value("sites['google.com'].pageRank.score");

TODO handle \ escape inside the array syntax

The long story

All our life is to handle data....with a loop. Let's start from a basic loop:

for(Iindata){data[I]=something(data[I]);}

To make jslint happy or ensure the loop correct, we should add property check:

for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}

In real life, data is not always ready. We must handle none data case:

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}}

Furthermore, please catch something() because they may throw some error.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I]=something(data[I]);}catch(E){handle_error(E);}}}}

The more assign in the loop, the more try/catch you need.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I].title=something(data[I].title);}catch(E){handle_error(E);}try{data[I].desciption=something(data[I].description);}catch(E){handle_error(E);}try{data[I].url=something(data[I].url);}catch(E){handle_error(E);}}}}

The loop becomes a nightmare now, right? Let's use lodash to reduce indents in the loop:

_(data).each(function(V){try{V.title=something(V.title);}catch(E){handle_error(E);}try{V.desciption=something(V.description);}catch(E){handle_error(E);}try{V.url=something(V.url);}catch(E){handle_error(E);}});

But, lodash still can not reduce the try/catch hell for you. Now, JsonPathProcessor help on this!

About

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

Resources

Stars

26 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

json-path-processor

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

npm versionnpm downloadDependency StatusBuild StatusTest CoverageCode ClimateLicense

Sauce Test Status

Installation

npm install json-path-processor

In browser:

<scriptsrc="dist/jpp.js"></script>

In an AMD loader:

require('jpp',function(jpp){/*....*/});

In nodejs:

varjpp=require('json-path-processor');

Features

Upgrade Notice

  • Now JPP stop using lodash and move to ES5. For none ES5 browsers/enviromnent, you should use polyfills to ensure JPP works well. You can try polyfill.io or polyfills.io.

  • Some lodash like method behavior changed, please refer to document.

Usage

varjpp=require('json-path-processor');// I wanna update all product titledata=jpp(data).each('product.title',function(V){returnsomething(V);}).value();// Ya, handle all product title and promotion description// almost all jpp methods are chainabledata=jpp(data).each('extra.promotion',function(O){O.description=someValue;returnO;}).each('product.title',someUtilFunc).value();// jpp play on the object reference so you even do not need to assign back!jpp(data).each('product',assignProductDefault);jpp(data).each('product',changeProductDetail);output(data);// chaining is cool, right?jpp(data).each('product',playTheProduct).each('product.title',fixProductTitle);output(data);

API document and example

  • jpp(data) : create the JPP chainning object by data.
varJ=jpp(['any','data',{or: {recursive: {'object'}}}]);
  • jpp(data, path) : a shortcut of jpp(data).value(path)
console.log(jpp({a: {b: 'OK'}},'a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}},'a.c.d'));// will get undefined
  • .value(path) : get value by JSON path. This method can not be chainned. When path is undefined or '' or '$', get whole data.
console.log(jpp([1,3,5]).value());// will get [1, 3, 5]console.log(jpp({a: {b: 'OK'}}).value('a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}}).value('a.c.d'));// will get undefined
  • .get(path) : get new JPP object by JSON path. All chainned methods on this is different from root object.
console.log(jpp({a: {b: 'OK'}}).get('a').get('b').value());// will get 'OK'
  • .set(path, value, create) : set new value by JSON path. When value is a function, execute the function with first argument as old value. the return value of the callback function will be assigned. When create exists, create new object by the JSON path, and create will be used as default value to be assigned when the callback function throws exception.
// will get {a: {b: 'OK', c:[1, 3]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.1',3).value());// will get {a: {b: 'BAD', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b','BAD').value());// will get {a: {b: 'OK', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?').value());// set failed ... WE CAN NOT CONVERT ARRAY TO OBJECTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?',true).value());// a.b.c[2 ~ 9] will become undefined ... ARRAY SIZE AUTO EXPEND IN JAVASCRIPTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.10','OK?',true).value());
  • .copy(from, to, skip) : copy value from one JSON path to another. When the JSON path not found, new object will be created. To prevent new object creation, pass skip as true as 3rd param.
// will get {a: {b: 'OK', c:[1, 4], d: 4}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).copy('a.c.1','a.d').value());
  • .del(path) : delete a key by JSON path. When the path exists, last key will be deleted; when it do not exist, do nothing.
// will get {a: {b: 'OK', c: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).del('a.b.c').value());// will get {a: {b: {}}console.log(jpp({a: {b: {c: {d: 2,q: 1}}}}).del('a.b.c').value());
  • .move(from, to) : move values from a path to another. when the origin path not found, do nothing.
// will get {a: {b: 'OK', d: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).move('a.c','a.d').value());
  • .range(path, args...) : Works like lodash.range() , create range and set the array into the path.
// will get {a: {b: 1, c: [0, 1, 2]}}console.log(jpp({a: {b: 1}}}).range('a.c',3).value());// will get {a: {b: 1, c: [3, 4]}}console.log(jpp({a: {b: 1}}}).range('a.c',3,5).value());// will get {a: {b: 1, c: [2, 5, 8, 11]}}console.log(jpp({a: {b: 1}}}).range('a.c',2,12,3).value());
  • .find(path, args...) : Works like lodash.find() , return the value. This method can not be chainned.
// will get 3console.log(jpp({a: {b: [0,3,4]}}}).find('a.b',function(O){returnO%2>0}));
  • .findLast(path, args...) : Works like lodash.findLast() , return the value. This method can not be chainned.
// will get 5console.log(jpp({a: {b: [1,3,4,5]}}}).find('a.b',function(O){returnO%2>0}));
  • .each(path, function (value, key) {...}) : Works like Array.map(), the callback arguments are: value, index. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not array. When your callback return undefined or throws, the item in array will not be changed.
console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V){returnV*2;}).value());// will get {a: {b: [2, 6, 10]}}console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V,I){// I as indexreturnV*I;}).value());// will get {a: {b: [0 , 3, 10]}}// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).each('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1 , 3, 5], c: 'ERROR'}}
  • .forIn(path, function (value, key) {...}) : Works like for (I in O), the callback arguments are: value, key. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not object. When your callback return undefined or throws, the item in object will not be changed.
// will get {a: 'OK!', b: 'BAD!', length: '9!'} // forIn() will not think object with length property as array.console.log(jpp({a: 'OK',b: 'BAD',length: 9}).forIn('$',function(V,I){returnV+'!';}).value());// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).forIn('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1, 3, 5], c: 'ERROR'}}
  • .filter(path, function (value, key) {...}) : Works like Array.filter() but also works well on object. the callback are: value, index|key. The filtered result will be assigned back to JPP object.
// will get {a: {b: {c: [3, 5], d:5}}}console.log(jpp({a: {b: {c: [2,3,4,5],d: 5}}}).filter('a.b.c',function(V){returnV%2>0;}).value());
  • .concat(path, path, path ...) : search for all values by proviced JSON path, then concat all arraies into one. None array value will be skipped. When array size >= 1, assign the concated array back to first path. Or, do nothing.
// will get {a: {b: {c: [1, 3], d:5}, d: [1, 3, 1,3]}}console.log(jpp({a: {b: {c: [1,3],d: 5}}}).concat('a.d','a.b.c','a.b.c').value());

Supported JSON Path

We only support absolute JSON Path and receive only one item.

  • $ : refer to self
  • $.foo.bar or .foo.bar or foo.bar or foo['bar'] or ['foo']['bar'] : refer to foo then bar
  • $.foo.3.bar or foo.3.bar or foo['3'].bar or foo['3']['bar'] : refer to foo then 4th item then bar

When you need to deal with . inside your property name, you can just use the array syntax:

// var result = data.sites['google.com'].pageRank.score;// prevent exception by jpp way:varresult=jpp(data).value("sites['google.com'].pageRank.score");

TODO handle \ escape inside the array syntax

The long story

All our life is to handle data....with a loop. Let's start from a basic loop:

for(Iindata){data[I]=something(data[I]);}

To make jslint happy or ensure the loop correct, we should add property check:

for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}

In real life, data is not always ready. We must handle none data case:

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}}

Furthermore, please catch something() because they may throw some error.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I]=something(data[I]);}catch(E){handle_error(E);}}}}

The more assign in the loop, the more try/catch you need.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I].title=something(data[I].title);}catch(E){handle_error(E);}try{data[I].desciption=something(data[I].description);}catch(E){handle_error(E);}try{data[I].url=something(data[I].url);}catch(E){handle_error(E);}}}}

The loop becomes a nightmare now, right? Let's use lodash to reduce indents in the loop:

_(data).each(function(V){try{V.title=something(V.title);}catch(E){handle_error(E);}try{V.desciption=something(V.description);}catch(E){handle_error(E);}try{V.url=something(V.url);}catch(E){handle_error(E);}});

But, lodash still can not reduce the try/catch hell for you. Now, JsonPathProcessor help on this!

About

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

Resources

Stars

26 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

json-path-processor

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

npm versionnpm downloadDependency StatusBuild StatusTest CoverageCode ClimateLicense

Sauce Test Status

Installation

npm install json-path-processor

In browser:

<scriptsrc="dist/jpp.js"></script>

In an AMD loader:

require('jpp',function(jpp){/*....*/});

In nodejs:

varjpp=require('json-path-processor');

Features

Upgrade Notice

  • Now JPP stop using lodash and move to ES5. For none ES5 browsers/enviromnent, you should use polyfills to ensure JPP works well. You can try polyfill.io or polyfills.io.

  • Some lodash like method behavior changed, please refer to document.

Usage

varjpp=require('json-path-processor');// I wanna update all product titledata=jpp(data).each('product.title',function(V){returnsomething(V);}).value();// Ya, handle all product title and promotion description// almost all jpp methods are chainabledata=jpp(data).each('extra.promotion',function(O){O.description=someValue;returnO;}).each('product.title',someUtilFunc).value();// jpp play on the object reference so you even do not need to assign back!jpp(data).each('product',assignProductDefault);jpp(data).each('product',changeProductDetail);output(data);// chaining is cool, right?jpp(data).each('product',playTheProduct).each('product.title',fixProductTitle);output(data);

API document and example

  • jpp(data) : create the JPP chainning object by data.
varJ=jpp(['any','data',{or: {recursive: {'object'}}}]);
  • jpp(data, path) : a shortcut of jpp(data).value(path)
console.log(jpp({a: {b: 'OK'}},'a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}},'a.c.d'));// will get undefined
  • .value(path) : get value by JSON path. This method can not be chainned. When path is undefined or '' or '$', get whole data.
console.log(jpp([1,3,5]).value());// will get [1, 3, 5]console.log(jpp({a: {b: 'OK'}}).value('a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}}).value('a.c.d'));// will get undefined
  • .get(path) : get new JPP object by JSON path. All chainned methods on this is different from root object.
console.log(jpp({a: {b: 'OK'}}).get('a').get('b').value());// will get 'OK'
  • .set(path, value, create) : set new value by JSON path. When value is a function, execute the function with first argument as old value. the return value of the callback function will be assigned. When create exists, create new object by the JSON path, and create will be used as default value to be assigned when the callback function throws exception.
// will get {a: {b: 'OK', c:[1, 3]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.1',3).value());// will get {a: {b: 'BAD', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b','BAD').value());// will get {a: {b: 'OK', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?').value());// set failed ... WE CAN NOT CONVERT ARRAY TO OBJECTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?',true).value());// a.b.c[2 ~ 9] will become undefined ... ARRAY SIZE AUTO EXPEND IN JAVASCRIPTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.10','OK?',true).value());
  • .copy(from, to, skip) : copy value from one JSON path to another. When the JSON path not found, new object will be created. To prevent new object creation, pass skip as true as 3rd param.
// will get {a: {b: 'OK', c:[1, 4], d: 4}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).copy('a.c.1','a.d').value());
  • .del(path) : delete a key by JSON path. When the path exists, last key will be deleted; when it do not exist, do nothing.
// will get {a: {b: 'OK', c: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).del('a.b.c').value());// will get {a: {b: {}}console.log(jpp({a: {b: {c: {d: 2,q: 1}}}}).del('a.b.c').value());
  • .move(from, to) : move values from a path to another. when the origin path not found, do nothing.
// will get {a: {b: 'OK', d: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).move('a.c','a.d').value());
  • .range(path, args...) : Works like lodash.range() , create range and set the array into the path.
// will get {a: {b: 1, c: [0, 1, 2]}}console.log(jpp({a: {b: 1}}}).range('a.c',3).value());// will get {a: {b: 1, c: [3, 4]}}console.log(jpp({a: {b: 1}}}).range('a.c',3,5).value());// will get {a: {b: 1, c: [2, 5, 8, 11]}}console.log(jpp({a: {b: 1}}}).range('a.c',2,12,3).value());
  • .find(path, args...) : Works like lodash.find() , return the value. This method can not be chainned.
// will get 3console.log(jpp({a: {b: [0,3,4]}}}).find('a.b',function(O){returnO%2>0}));
  • .findLast(path, args...) : Works like lodash.findLast() , return the value. This method can not be chainned.
// will get 5console.log(jpp({a: {b: [1,3,4,5]}}}).find('a.b',function(O){returnO%2>0}));
  • .each(path, function (value, key) {...}) : Works like Array.map(), the callback arguments are: value, index. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not array. When your callback return undefined or throws, the item in array will not be changed.
console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V){returnV*2;}).value());// will get {a: {b: [2, 6, 10]}}console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V,I){// I as indexreturnV*I;}).value());// will get {a: {b: [0 , 3, 10]}}// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).each('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1 , 3, 5], c: 'ERROR'}}
  • .forIn(path, function (value, key) {...}) : Works like for (I in O), the callback arguments are: value, key. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not object. When your callback return undefined or throws, the item in object will not be changed.
// will get {a: 'OK!', b: 'BAD!', length: '9!'} // forIn() will not think object with length property as array.console.log(jpp({a: 'OK',b: 'BAD',length: 9}).forIn('$',function(V,I){returnV+'!';}).value());// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).forIn('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1, 3, 5], c: 'ERROR'}}
  • .filter(path, function (value, key) {...}) : Works like Array.filter() but also works well on object. the callback are: value, index|key. The filtered result will be assigned back to JPP object.
// will get {a: {b: {c: [3, 5], d:5}}}console.log(jpp({a: {b: {c: [2,3,4,5],d: 5}}}).filter('a.b.c',function(V){returnV%2>0;}).value());
  • .concat(path, path, path ...) : search for all values by proviced JSON path, then concat all arraies into one. None array value will be skipped. When array size >= 1, assign the concated array back to first path. Or, do nothing.
// will get {a: {b: {c: [1, 3], d:5}, d: [1, 3, 1,3]}}console.log(jpp({a: {b: {c: [1,3],d: 5}}}).concat('a.d','a.b.c','a.b.c').value());

Supported JSON Path

We only support absolute JSON Path and receive only one item.

  • $ : refer to self
  • $.foo.bar or .foo.bar or foo.bar or foo['bar'] or ['foo']['bar'] : refer to foo then bar
  • $.foo.3.bar or foo.3.bar or foo['3'].bar or foo['3']['bar'] : refer to foo then 4th item then bar

When you need to deal with . inside your property name, you can just use the array syntax:

// var result = data.sites['google.com'].pageRank.score;// prevent exception by jpp way:varresult=jpp(data).value("sites['google.com'].pageRank.score");

TODO handle \ escape inside the array syntax

The long story

All our life is to handle data....with a loop. Let's start from a basic loop:

for(Iindata){data[I]=something(data[I]);}

To make jslint happy or ensure the loop correct, we should add property check:

for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}

In real life, data is not always ready. We must handle none data case:

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}}

Furthermore, please catch something() because they may throw some error.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I]=something(data[I]);}catch(E){handle_error(E);}}}}

The more assign in the loop, the more try/catch you need.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I].title=something(data[I].title);}catch(E){handle_error(E);}try{data[I].desciption=something(data[I].description);}catch(E){handle_error(E);}try{data[I].url=something(data[I].url);}catch(E){handle_error(E);}}}}

The loop becomes a nightmare now, right? Let's use lodash to reduce indents in the loop:

_(data).each(function(V){try{V.title=something(V.title);}catch(E){handle_error(E);}try{V.desciption=something(V.description);}catch(E){handle_error(E);}try{V.url=something(V.url);}catch(E){handle_error(E);}});

But, lodash still can not reduce the try/catch hell for you. Now, JsonPathProcessor help on this!

About

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

Resources

Stars

26 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

json-path-processor

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

npm versionnpm downloadDependency StatusBuild StatusTest CoverageCode ClimateLicense

Sauce Test Status

Installation

npm install json-path-processor

In browser:

<scriptsrc="dist/jpp.js"></script>

In an AMD loader:

require('jpp',function(jpp){/*....*/});

In nodejs:

varjpp=require('json-path-processor');

Features

Upgrade Notice

  • Now JPP stop using lodash and move to ES5. For none ES5 browsers/enviromnent, you should use polyfills to ensure JPP works well. You can try polyfill.io or polyfills.io.

  • Some lodash like method behavior changed, please refer to document.

Usage

varjpp=require('json-path-processor');// I wanna update all product titledata=jpp(data).each('product.title',function(V){returnsomething(V);}).value();// Ya, handle all product title and promotion description// almost all jpp methods are chainabledata=jpp(data).each('extra.promotion',function(O){O.description=someValue;returnO;}).each('product.title',someUtilFunc).value();// jpp play on the object reference so you even do not need to assign back!jpp(data).each('product',assignProductDefault);jpp(data).each('product',changeProductDetail);output(data);// chaining is cool, right?jpp(data).each('product',playTheProduct).each('product.title',fixProductTitle);output(data);

API document and example

  • jpp(data) : create the JPP chainning object by data.
varJ=jpp(['any','data',{or: {recursive: {'object'}}}]);
  • jpp(data, path) : a shortcut of jpp(data).value(path)
console.log(jpp({a: {b: 'OK'}},'a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}},'a.c.d'));// will get undefined
  • .value(path) : get value by JSON path. This method can not be chainned. When path is undefined or '' or '$', get whole data.
console.log(jpp([1,3,5]).value());// will get [1, 3, 5]console.log(jpp({a: {b: 'OK'}}).value('a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}}).value('a.c.d'));// will get undefined
  • .get(path) : get new JPP object by JSON path. All chainned methods on this is different from root object.
console.log(jpp({a: {b: 'OK'}}).get('a').get('b').value());// will get 'OK'
  • .set(path, value, create) : set new value by JSON path. When value is a function, execute the function with first argument as old value. the return value of the callback function will be assigned. When create exists, create new object by the JSON path, and create will be used as default value to be assigned when the callback function throws exception.
// will get {a: {b: 'OK', c:[1, 3]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.1',3).value());// will get {a: {b: 'BAD', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b','BAD').value());// will get {a: {b: 'OK', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?').value());// set failed ... WE CAN NOT CONVERT ARRAY TO OBJECTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?',true).value());// a.b.c[2 ~ 9] will become undefined ... ARRAY SIZE AUTO EXPEND IN JAVASCRIPTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.10','OK?',true).value());
  • .copy(from, to, skip) : copy value from one JSON path to another. When the JSON path not found, new object will be created. To prevent new object creation, pass skip as true as 3rd param.
// will get {a: {b: 'OK', c:[1, 4], d: 4}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).copy('a.c.1','a.d').value());
  • .del(path) : delete a key by JSON path. When the path exists, last key will be deleted; when it do not exist, do nothing.
// will get {a: {b: 'OK', c: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).del('a.b.c').value());// will get {a: {b: {}}console.log(jpp({a: {b: {c: {d: 2,q: 1}}}}).del('a.b.c').value());
  • .move(from, to) : move values from a path to another. when the origin path not found, do nothing.
// will get {a: {b: 'OK', d: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).move('a.c','a.d').value());
  • .range(path, args...) : Works like lodash.range() , create range and set the array into the path.
// will get {a: {b: 1, c: [0, 1, 2]}}console.log(jpp({a: {b: 1}}}).range('a.c',3).value());// will get {a: {b: 1, c: [3, 4]}}console.log(jpp({a: {b: 1}}}).range('a.c',3,5).value());// will get {a: {b: 1, c: [2, 5, 8, 11]}}console.log(jpp({a: {b: 1}}}).range('a.c',2,12,3).value());
  • .find(path, args...) : Works like lodash.find() , return the value. This method can not be chainned.
// will get 3console.log(jpp({a: {b: [0,3,4]}}}).find('a.b',function(O){returnO%2>0}));
  • .findLast(path, args...) : Works like lodash.findLast() , return the value. This method can not be chainned.
// will get 5console.log(jpp({a: {b: [1,3,4,5]}}}).find('a.b',function(O){returnO%2>0}));
  • .each(path, function (value, key) {...}) : Works like Array.map(), the callback arguments are: value, index. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not array. When your callback return undefined or throws, the item in array will not be changed.
console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V){returnV*2;}).value());// will get {a: {b: [2, 6, 10]}}console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V,I){// I as indexreturnV*I;}).value());// will get {a: {b: [0 , 3, 10]}}// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).each('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1 , 3, 5], c: 'ERROR'}}
  • .forIn(path, function (value, key) {...}) : Works like for (I in O), the callback arguments are: value, key. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not object. When your callback return undefined or throws, the item in object will not be changed.
// will get {a: 'OK!', b: 'BAD!', length: '9!'} // forIn() will not think object with length property as array.console.log(jpp({a: 'OK',b: 'BAD',length: 9}).forIn('$',function(V,I){returnV+'!';}).value());// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).forIn('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1, 3, 5], c: 'ERROR'}}
  • .filter(path, function (value, key) {...}) : Works like Array.filter() but also works well on object. the callback are: value, index|key. The filtered result will be assigned back to JPP object.
// will get {a: {b: {c: [3, 5], d:5}}}console.log(jpp({a: {b: {c: [2,3,4,5],d: 5}}}).filter('a.b.c',function(V){returnV%2>0;}).value());
  • .concat(path, path, path ...) : search for all values by proviced JSON path, then concat all arraies into one. None array value will be skipped. When array size >= 1, assign the concated array back to first path. Or, do nothing.
// will get {a: {b: {c: [1, 3], d:5}, d: [1, 3, 1,3]}}console.log(jpp({a: {b: {c: [1,3],d: 5}}}).concat('a.d','a.b.c','a.b.c').value());

Supported JSON Path

We only support absolute JSON Path and receive only one item.

  • $ : refer to self
  • $.foo.bar or .foo.bar or foo.bar or foo['bar'] or ['foo']['bar'] : refer to foo then bar
  • $.foo.3.bar or foo.3.bar or foo['3'].bar or foo['3']['bar'] : refer to foo then 4th item then bar

When you need to deal with . inside your property name, you can just use the array syntax:

// var result = data.sites['google.com'].pageRank.score;// prevent exception by jpp way:varresult=jpp(data).value("sites['google.com'].pageRank.score");

TODO handle \ escape inside the array syntax

The long story

All our life is to handle data....with a loop. Let's start from a basic loop:

for(Iindata){data[I]=something(data[I]);}

To make jslint happy or ensure the loop correct, we should add property check:

for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}

In real life, data is not always ready. We must handle none data case:

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}}

Furthermore, please catch something() because they may throw some error.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I]=something(data[I]);}catch(E){handle_error(E);}}}}

The more assign in the loop, the more try/catch you need.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I].title=something(data[I].title);}catch(E){handle_error(E);}try{data[I].desciption=something(data[I].description);}catch(E){handle_error(E);}try{data[I].url=something(data[I].url);}catch(E){handle_error(E);}}}}

The loop becomes a nightmare now, right? Let's use lodash to reduce indents in the loop:

_(data).each(function(V){try{V.title=something(V.title);}catch(E){handle_error(E);}try{V.desciption=something(V.description);}catch(E){handle_error(E);}try{V.url=something(V.url);}catch(E){handle_error(E);}});

But, lodash still can not reduce the try/catch hell for you. Now, JsonPathProcessor help on this!

About

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

Resources

Stars

26 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

json-path-processor

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

npm versionnpm downloadDependency StatusBuild StatusTest CoverageCode ClimateLicense

Sauce Test Status

Installation

npm install json-path-processor

In browser:

<scriptsrc="dist/jpp.js"></script>

In an AMD loader:

require('jpp',function(jpp){/*....*/});

In nodejs:

varjpp=require('json-path-processor');

Features

Upgrade Notice

  • Now JPP stop using lodash and move to ES5. For none ES5 browsers/enviromnent, you should use polyfills to ensure JPP works well. You can try polyfill.io or polyfills.io.

  • Some lodash like method behavior changed, please refer to document.

Usage

varjpp=require('json-path-processor');// I wanna update all product titledata=jpp(data).each('product.title',function(V){returnsomething(V);}).value();// Ya, handle all product title and promotion description// almost all jpp methods are chainabledata=jpp(data).each('extra.promotion',function(O){O.description=someValue;returnO;}).each('product.title',someUtilFunc).value();// jpp play on the object reference so you even do not need to assign back!jpp(data).each('product',assignProductDefault);jpp(data).each('product',changeProductDetail);output(data);// chaining is cool, right?jpp(data).each('product',playTheProduct).each('product.title',fixProductTitle);output(data);

API document and example

  • jpp(data) : create the JPP chainning object by data.
varJ=jpp(['any','data',{or: {recursive: {'object'}}}]);
  • jpp(data, path) : a shortcut of jpp(data).value(path)
console.log(jpp({a: {b: 'OK'}},'a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}},'a.c.d'));// will get undefined
  • .value(path) : get value by JSON path. This method can not be chainned. When path is undefined or '' or '$', get whole data.
console.log(jpp([1,3,5]).value());// will get [1, 3, 5]console.log(jpp({a: {b: 'OK'}}).value('a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}}).value('a.c.d'));// will get undefined
  • .get(path) : get new JPP object by JSON path. All chainned methods on this is different from root object.
console.log(jpp({a: {b: 'OK'}}).get('a').get('b').value());// will get 'OK'
  • .set(path, value, create) : set new value by JSON path. When value is a function, execute the function with first argument as old value. the return value of the callback function will be assigned. When create exists, create new object by the JSON path, and create will be used as default value to be assigned when the callback function throws exception.
// will get {a: {b: 'OK', c:[1, 3]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.1',3).value());// will get {a: {b: 'BAD', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b','BAD').value());// will get {a: {b: 'OK', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?').value());// set failed ... WE CAN NOT CONVERT ARRAY TO OBJECTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?',true).value());// a.b.c[2 ~ 9] will become undefined ... ARRAY SIZE AUTO EXPEND IN JAVASCRIPTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.10','OK?',true).value());
  • .copy(from, to, skip) : copy value from one JSON path to another. When the JSON path not found, new object will be created. To prevent new object creation, pass skip as true as 3rd param.
// will get {a: {b: 'OK', c:[1, 4], d: 4}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).copy('a.c.1','a.d').value());
  • .del(path) : delete a key by JSON path. When the path exists, last key will be deleted; when it do not exist, do nothing.
// will get {a: {b: 'OK', c: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).del('a.b.c').value());// will get {a: {b: {}}console.log(jpp({a: {b: {c: {d: 2,q: 1}}}}).del('a.b.c').value());
  • .move(from, to) : move values from a path to another. when the origin path not found, do nothing.
// will get {a: {b: 'OK', d: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).move('a.c','a.d').value());
  • .range(path, args...) : Works like lodash.range() , create range and set the array into the path.
// will get {a: {b: 1, c: [0, 1, 2]}}console.log(jpp({a: {b: 1}}}).range('a.c',3).value());// will get {a: {b: 1, c: [3, 4]}}console.log(jpp({a: {b: 1}}}).range('a.c',3,5).value());// will get {a: {b: 1, c: [2, 5, 8, 11]}}console.log(jpp({a: {b: 1}}}).range('a.c',2,12,3).value());
  • .find(path, args...) : Works like lodash.find() , return the value. This method can not be chainned.
// will get 3console.log(jpp({a: {b: [0,3,4]}}}).find('a.b',function(O){returnO%2>0}));
  • .findLast(path, args...) : Works like lodash.findLast() , return the value. This method can not be chainned.
// will get 5console.log(jpp({a: {b: [1,3,4,5]}}}).find('a.b',function(O){returnO%2>0}));
  • .each(path, function (value, key) {...}) : Works like Array.map(), the callback arguments are: value, index. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not array. When your callback return undefined or throws, the item in array will not be changed.
console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V){returnV*2;}).value());// will get {a: {b: [2, 6, 10]}}console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V,I){// I as indexreturnV*I;}).value());// will get {a: {b: [0 , 3, 10]}}// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).each('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1 , 3, 5], c: 'ERROR'}}
  • .forIn(path, function (value, key) {...}) : Works like for (I in O), the callback arguments are: value, key. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not object. When your callback return undefined or throws, the item in object will not be changed.
// will get {a: 'OK!', b: 'BAD!', length: '9!'} // forIn() will not think object with length property as array.console.log(jpp({a: 'OK',b: 'BAD',length: 9}).forIn('$',function(V,I){returnV+'!';}).value());// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).forIn('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1, 3, 5], c: 'ERROR'}}
  • .filter(path, function (value, key) {...}) : Works like Array.filter() but also works well on object. the callback are: value, index|key. The filtered result will be assigned back to JPP object.
// will get {a: {b: {c: [3, 5], d:5}}}console.log(jpp({a: {b: {c: [2,3,4,5],d: 5}}}).filter('a.b.c',function(V){returnV%2>0;}).value());
  • .concat(path, path, path ...) : search for all values by proviced JSON path, then concat all arraies into one. None array value will be skipped. When array size >= 1, assign the concated array back to first path. Or, do nothing.
// will get {a: {b: {c: [1, 3], d:5}, d: [1, 3, 1,3]}}console.log(jpp({a: {b: {c: [1,3],d: 5}}}).concat('a.d','a.b.c','a.b.c').value());

Supported JSON Path

We only support absolute JSON Path and receive only one item.

  • $ : refer to self
  • $.foo.bar or .foo.bar or foo.bar or foo['bar'] or ['foo']['bar'] : refer to foo then bar
  • $.foo.3.bar or foo.3.bar or foo['3'].bar or foo['3']['bar'] : refer to foo then 4th item then bar

When you need to deal with . inside your property name, you can just use the array syntax:

// var result = data.sites['google.com'].pageRank.score;// prevent exception by jpp way:varresult=jpp(data).value("sites['google.com'].pageRank.score");

TODO handle \ escape inside the array syntax

The long story

All our life is to handle data....with a loop. Let's start from a basic loop:

for(Iindata){data[I]=something(data[I]);}

To make jslint happy or ensure the loop correct, we should add property check:

for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}

In real life, data is not always ready. We must handle none data case:

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}}

Furthermore, please catch something() because they may throw some error.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I]=something(data[I]);}catch(E){handle_error(E);}}}}

The more assign in the loop, the more try/catch you need.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I].title=something(data[I].title);}catch(E){handle_error(E);}try{data[I].desciption=something(data[I].description);}catch(E){handle_error(E);}try{data[I].url=something(data[I].url);}catch(E){handle_error(E);}}}}

The loop becomes a nightmare now, right? Let's use lodash to reduce indents in the loop:

_(data).each(function(V){try{V.title=something(V.title);}catch(E){handle_error(E);}try{V.desciption=something(V.description);}catch(E){handle_error(E);}try{V.url=something(V.url);}catch(E){handle_error(E);}});

But, lodash still can not reduce the try/catch hell for you. Now, JsonPathProcessor help on this!

About

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

Resources

Stars

26 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

json-path-processor

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

npm versionnpm downloadDependency StatusBuild StatusTest CoverageCode ClimateLicense

Sauce Test Status

Installation

npm install json-path-processor

In browser:

<scriptsrc="dist/jpp.js"></script>

In an AMD loader:

require('jpp',function(jpp){/*....*/});

In nodejs:

varjpp=require('json-path-processor');

Features

Upgrade Notice

  • Now JPP stop using lodash and move to ES5. For none ES5 browsers/enviromnent, you should use polyfills to ensure JPP works well. You can try polyfill.io or polyfills.io.

  • Some lodash like method behavior changed, please refer to document.

Usage

varjpp=require('json-path-processor');// I wanna update all product titledata=jpp(data).each('product.title',function(V){returnsomething(V);}).value();// Ya, handle all product title and promotion description// almost all jpp methods are chainabledata=jpp(data).each('extra.promotion',function(O){O.description=someValue;returnO;}).each('product.title',someUtilFunc).value();// jpp play on the object reference so you even do not need to assign back!jpp(data).each('product',assignProductDefault);jpp(data).each('product',changeProductDetail);output(data);// chaining is cool, right?jpp(data).each('product',playTheProduct).each('product.title',fixProductTitle);output(data);

API document and example

  • jpp(data) : create the JPP chainning object by data.
varJ=jpp(['any','data',{or: {recursive: {'object'}}}]);
  • jpp(data, path) : a shortcut of jpp(data).value(path)
console.log(jpp({a: {b: 'OK'}},'a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}},'a.c.d'));// will get undefined
  • .value(path) : get value by JSON path. This method can not be chainned. When path is undefined or '' or '$', get whole data.
console.log(jpp([1,3,5]).value());// will get [1, 3, 5]console.log(jpp({a: {b: 'OK'}}).value('a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}}).value('a.c.d'));// will get undefined
  • .get(path) : get new JPP object by JSON path. All chainned methods on this is different from root object.
console.log(jpp({a: {b: 'OK'}}).get('a').get('b').value());// will get 'OK'
  • .set(path, value, create) : set new value by JSON path. When value is a function, execute the function with first argument as old value. the return value of the callback function will be assigned. When create exists, create new object by the JSON path, and create will be used as default value to be assigned when the callback function throws exception.
// will get {a: {b: 'OK', c:[1, 3]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.1',3).value());// will get {a: {b: 'BAD', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b','BAD').value());// will get {a: {b: 'OK', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?').value());// set failed ... WE CAN NOT CONVERT ARRAY TO OBJECTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?',true).value());// a.b.c[2 ~ 9] will become undefined ... ARRAY SIZE AUTO EXPEND IN JAVASCRIPTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.10','OK?',true).value());
  • .copy(from, to, skip) : copy value from one JSON path to another. When the JSON path not found, new object will be created. To prevent new object creation, pass skip as true as 3rd param.
// will get {a: {b: 'OK', c:[1, 4], d: 4}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).copy('a.c.1','a.d').value());
  • .del(path) : delete a key by JSON path. When the path exists, last key will be deleted; when it do not exist, do nothing.
// will get {a: {b: 'OK', c: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).del('a.b.c').value());// will get {a: {b: {}}console.log(jpp({a: {b: {c: {d: 2,q: 1}}}}).del('a.b.c').value());
  • .move(from, to) : move values from a path to another. when the origin path not found, do nothing.
// will get {a: {b: 'OK', d: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).move('a.c','a.d').value());
  • .range(path, args...) : Works like lodash.range() , create range and set the array into the path.
// will get {a: {b: 1, c: [0, 1, 2]}}console.log(jpp({a: {b: 1}}}).range('a.c',3).value());// will get {a: {b: 1, c: [3, 4]}}console.log(jpp({a: {b: 1}}}).range('a.c',3,5).value());// will get {a: {b: 1, c: [2, 5, 8, 11]}}console.log(jpp({a: {b: 1}}}).range('a.c',2,12,3).value());
  • .find(path, args...) : Works like lodash.find() , return the value. This method can not be chainned.
// will get 3console.log(jpp({a: {b: [0,3,4]}}}).find('a.b',function(O){returnO%2>0}));
  • .findLast(path, args...) : Works like lodash.findLast() , return the value. This method can not be chainned.
// will get 5console.log(jpp({a: {b: [1,3,4,5]}}}).find('a.b',function(O){returnO%2>0}));
  • .each(path, function (value, key) {...}) : Works like Array.map(), the callback arguments are: value, index. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not array. When your callback return undefined or throws, the item in array will not be changed.
console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V){returnV*2;}).value());// will get {a: {b: [2, 6, 10]}}console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V,I){// I as indexreturnV*I;}).value());// will get {a: {b: [0 , 3, 10]}}// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).each('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1 , 3, 5], c: 'ERROR'}}
  • .forIn(path, function (value, key) {...}) : Works like for (I in O), the callback arguments are: value, key. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not object. When your callback return undefined or throws, the item in object will not be changed.
// will get {a: 'OK!', b: 'BAD!', length: '9!'} // forIn() will not think object with length property as array.console.log(jpp({a: 'OK',b: 'BAD',length: 9}).forIn('$',function(V,I){returnV+'!';}).value());// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).forIn('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1, 3, 5], c: 'ERROR'}}
  • .filter(path, function (value, key) {...}) : Works like Array.filter() but also works well on object. the callback are: value, index|key. The filtered result will be assigned back to JPP object.
// will get {a: {b: {c: [3, 5], d:5}}}console.log(jpp({a: {b: {c: [2,3,4,5],d: 5}}}).filter('a.b.c',function(V){returnV%2>0;}).value());
  • .concat(path, path, path ...) : search for all values by proviced JSON path, then concat all arraies into one. None array value will be skipped. When array size >= 1, assign the concated array back to first path. Or, do nothing.
// will get {a: {b: {c: [1, 3], d:5}, d: [1, 3, 1,3]}}console.log(jpp({a: {b: {c: [1,3],d: 5}}}).concat('a.d','a.b.c','a.b.c').value());

Supported JSON Path

We only support absolute JSON Path and receive only one item.

  • $ : refer to self
  • $.foo.bar or .foo.bar or foo.bar or foo['bar'] or ['foo']['bar'] : refer to foo then bar
  • $.foo.3.bar or foo.3.bar or foo['3'].bar or foo['3']['bar'] : refer to foo then 4th item then bar

When you need to deal with . inside your property name, you can just use the array syntax:

// var result = data.sites['google.com'].pageRank.score;// prevent exception by jpp way:varresult=jpp(data).value("sites['google.com'].pageRank.score");

TODO handle \ escape inside the array syntax

The long story

All our life is to handle data....with a loop. Let's start from a basic loop:

for(Iindata){data[I]=something(data[I]);}

To make jslint happy or ensure the loop correct, we should add property check:

for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}

In real life, data is not always ready. We must handle none data case:

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}}

Furthermore, please catch something() because they may throw some error.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I]=something(data[I]);}catch(E){handle_error(E);}}}}

The more assign in the loop, the more try/catch you need.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I].title=something(data[I].title);}catch(E){handle_error(E);}try{data[I].desciption=something(data[I].description);}catch(E){handle_error(E);}try{data[I].url=something(data[I].url);}catch(E){handle_error(E);}}}}

The loop becomes a nightmare now, right? Let's use lodash to reduce indents in the loop:

_(data).each(function(V){try{V.title=something(V.title);}catch(E){handle_error(E);}try{V.desciption=something(V.description);}catch(E){handle_error(E);}try{V.url=something(V.url);}catch(E){handle_error(E);}});

But, lodash still can not reduce the try/catch hell for you. Now, JsonPathProcessor help on this!

About

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

Resources

Stars

26 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

json-path-processor

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

npm versionnpm downloadDependency StatusBuild StatusTest CoverageCode ClimateLicense

Sauce Test Status

Installation

npm install json-path-processor

In browser:

<scriptsrc="dist/jpp.js"></script>

In an AMD loader:

require('jpp',function(jpp){/*....*/});

In nodejs:

varjpp=require('json-path-processor');

Features

Upgrade Notice

  • Now JPP stop using lodash and move to ES5. For none ES5 browsers/enviromnent, you should use polyfills to ensure JPP works well. You can try polyfill.io or polyfills.io.

  • Some lodash like method behavior changed, please refer to document.

Usage

varjpp=require('json-path-processor');// I wanna update all product titledata=jpp(data).each('product.title',function(V){returnsomething(V);}).value();// Ya, handle all product title and promotion description// almost all jpp methods are chainabledata=jpp(data).each('extra.promotion',function(O){O.description=someValue;returnO;}).each('product.title',someUtilFunc).value();// jpp play on the object reference so you even do not need to assign back!jpp(data).each('product',assignProductDefault);jpp(data).each('product',changeProductDetail);output(data);// chaining is cool, right?jpp(data).each('product',playTheProduct).each('product.title',fixProductTitle);output(data);

API document and example

  • jpp(data) : create the JPP chainning object by data.
varJ=jpp(['any','data',{or: {recursive: {'object'}}}]);
  • jpp(data, path) : a shortcut of jpp(data).value(path)
console.log(jpp({a: {b: 'OK'}},'a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}},'a.c.d'));// will get undefined
  • .value(path) : get value by JSON path. This method can not be chainned. When path is undefined or '' or '$', get whole data.
console.log(jpp([1,3,5]).value());// will get [1, 3, 5]console.log(jpp({a: {b: 'OK'}}).value('a.b'));// will get 'OK'console.log(jpp({a: {b: 'OK'}}).value('a.c.d'));// will get undefined
  • .get(path) : get new JPP object by JSON path. All chainned methods on this is different from root object.
console.log(jpp({a: {b: 'OK'}}).get('a').get('b').value());// will get 'OK'
  • .set(path, value, create) : set new value by JSON path. When value is a function, execute the function with first argument as old value. the return value of the callback function will be assigned. When create exists, create new object by the JSON path, and create will be used as default value to be assigned when the callback function throws exception.
// will get {a: {b: 'OK', c:[1, 3]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.1',3).value());// will get {a: {b: 'BAD', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b','BAD').value());// will get {a: {b: 'OK', c:[1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?').value());// set failed ... WE CAN NOT CONVERT ARRAY TO OBJECTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.b.c.d','OK?',true).value());// a.b.c[2 ~ 9] will become undefined ... ARRAY SIZE AUTO EXPEND IN JAVASCRIPTconsole.log(jpp({a: {b: 'OK',c: [1,4]}}).set('a.c.10','OK?',true).value());
  • .copy(from, to, skip) : copy value from one JSON path to another. When the JSON path not found, new object will be created. To prevent new object creation, pass skip as true as 3rd param.
// will get {a: {b: 'OK', c:[1, 4], d: 4}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).copy('a.c.1','a.d').value());
  • .del(path) : delete a key by JSON path. When the path exists, last key will be deleted; when it do not exist, do nothing.
// will get {a: {b: 'OK', c: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).del('a.b.c').value());// will get {a: {b: {}}console.log(jpp({a: {b: {c: {d: 2,q: 1}}}}).del('a.b.c').value());
  • .move(from, to) : move values from a path to another. when the origin path not found, do nothing.
// will get {a: {b: 'OK', d: [1, 4]}}console.log(jpp({a: {b: 'OK',c: [1,4]}}).move('a.c','a.d').value());
  • .range(path, args...) : Works like lodash.range() , create range and set the array into the path.
// will get {a: {b: 1, c: [0, 1, 2]}}console.log(jpp({a: {b: 1}}}).range('a.c',3).value());// will get {a: {b: 1, c: [3, 4]}}console.log(jpp({a: {b: 1}}}).range('a.c',3,5).value());// will get {a: {b: 1, c: [2, 5, 8, 11]}}console.log(jpp({a: {b: 1}}}).range('a.c',2,12,3).value());
  • .find(path, args...) : Works like lodash.find() , return the value. This method can not be chainned.
// will get 3console.log(jpp({a: {b: [0,3,4]}}}).find('a.b',function(O){returnO%2>0}));
  • .findLast(path, args...) : Works like lodash.findLast() , return the value. This method can not be chainned.
// will get 5console.log(jpp({a: {b: [1,3,4,5]}}}).find('a.b',function(O){returnO%2>0}));
  • .each(path, function (value, key) {...}) : Works like Array.map(), the callback arguments are: value, index. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not array. When your callback return undefined or throws, the item in array will not be changed.
console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V){returnV*2;}).value());// will get {a: {b: [2, 6, 10]}}console.log(jpp({a: {b: [1,3,5]}}).each('a.b',function(V,I){// I as indexreturnV*I;}).value());// will get {a: {b: [0 , 3, 10]}}// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).each('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1 , 3, 5], c: 'ERROR'}}
  • .forIn(path, function (value, key) {...}) : Works like for (I in O), the callback arguments are: value, key. The return value of callback will be assigned back to JPP object. You can apply second callback function for fallback when the path is not found or not object. When your callback return undefined or throws, the item in object will not be changed.
// will get {a: 'OK!', b: 'BAD!', length: '9!'} // forIn() will not think object with length property as array.console.log(jpp({a: 'OK',b: 'BAD',length: 9}).forIn('$',function(V,I){returnV+'!';}).value());// fallback when a.c is not array or not object or not foundconsole.log(jpp({a: {b: [1,3,5]}}).forIn('a.c',function(V){returnV*I;},function(O){return'ERROR'}).value());// will get {a: {b: [1, 3, 5], c: 'ERROR'}}
  • .filter(path, function (value, key) {...}) : Works like Array.filter() but also works well on object. the callback are: value, index|key. The filtered result will be assigned back to JPP object.
// will get {a: {b: {c: [3, 5], d:5}}}console.log(jpp({a: {b: {c: [2,3,4,5],d: 5}}}).filter('a.b.c',function(V){returnV%2>0;}).value());
  • .concat(path, path, path ...) : search for all values by proviced JSON path, then concat all arraies into one. None array value will be skipped. When array size >= 1, assign the concated array back to first path. Or, do nothing.
// will get {a: {b: {c: [1, 3], d:5}, d: [1, 3, 1,3]}}console.log(jpp({a: {b: {c: [1,3],d: 5}}}).concat('a.d','a.b.c','a.b.c').value());

Supported JSON Path

We only support absolute JSON Path and receive only one item.

  • $ : refer to self
  • $.foo.bar or .foo.bar or foo.bar or foo['bar'] or ['foo']['bar'] : refer to foo then bar
  • $.foo.3.bar or foo.3.bar or foo['3'].bar or foo['3']['bar'] : refer to foo then 4th item then bar

When you need to deal with . inside your property name, you can just use the array syntax:

// var result = data.sites['google.com'].pageRank.score;// prevent exception by jpp way:varresult=jpp(data).value("sites['google.com'].pageRank.score");

TODO handle \ escape inside the array syntax

The long story

All our life is to handle data....with a loop. Let's start from a basic loop:

for(Iindata){data[I]=something(data[I]);}

To make jslint happy or ensure the loop correct, we should add property check:

for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}

In real life, data is not always ready. We must handle none data case:

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){data[I]=something(data[I]);}}}

Furthermore, please catch something() because they may throw some error.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I]=something(data[I]);}catch(E){handle_error(E);}}}}

The more assign in the loop, the more try/catch you need.

if(data&&is_object(data)){for(Iindata){if(data.hasOwnProperty(I)){try{data[I].title=something(data[I].title);}catch(E){handle_error(E);}try{data[I].desciption=something(data[I].description);}catch(E){handle_error(E);}try{data[I].url=something(data[I].url);}catch(E){handle_error(E);}}}}

The loop becomes a nightmare now, right? Let's use lodash to reduce indents in the loop:

_(data).each(function(V){try{V.title=something(V.title);}catch(E){handle_error(E);}try{V.desciption=something(V.description);}catch(E){handle_error(E);}try{V.url=something(V.url);}catch(E){handle_error(E);}});

But, lodash still can not reduce the try/catch hell for you. Now, JsonPathProcessor help on this!

About

JsonPathProcessor (JPP) - A common data processor target to process data without running try catch hell, and get/set properties of nested objects.

Resources

Stars

26 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages