jsonway
v0.4.0
Published
JSONway - object value getter & setter from path strings
Maintainers
Readme
JSONway
Find and filter nested values from JSON using JSONway's concise syntax, including conditional expressions. Same syntax also available as a setter path, and other useful functions to use with deeply nested JSON values.
Install
npm install jsonwayUsage
import JSONway from 'jsonway'
const myObject = {
aa: { cc: { ff: 42, gg: true }, dd: 'value' },
bb: [ { ff: 10 }, { ff: -5, gg: 'another' } ]
}
const result = JSONway.get(myObject, '**.ff(>=10)')
// [ 42, 10 ]
const tableQuery = `[{
ff: bb[*].ff,
aa.cc.ff,
aa.dd,
}]
`
const table = JSONway.get(myObject, tableQuery)
// [
// { ff: 10, 'aa.dd': 'value', 'aa.cc.ff': 42 },
// { ff: -5, 'aa.dd': 'value', 'aa.cc.ff': 42 }
// ]
JSONway.set(table, '[*].gg', 'yes')
// [
// { ff: 10, 'aa.dd': 'value', 'aa.cc.ff': 42, gg: 'yes' },
// { ff: -5, 'aa.dd': 'value', 'aa.cc.ff': 42, gg: 'yes' }
// ]Syntax
In general spaces and new lines are ignored and trimmed in the syntax, so that especially long paths don't need to be crammed into a one-liner.
Overview
a.b.ca value from nested objects.a[].ball thebproperties within a list of objects.a[].b[].call values ofcas a flat list.a[#]unique values of lista.a[#].bunique values ofb.a[:].b[:].ca list of lists of values ofc(keeping structure).a[].b(>10)allbvalues that are larger than10.a[](b > 10 || c != 'foo').ballbvalues wherecalso is not"foo".a[0]first value from lista.a[-1]last value.a[-2:]last 2 values.a[:3]first 3 values.a[1:6]first 5 values while skipping the first.a[0,2,-1]first, third, and last values in a list.a.**.callcvalues regardless where they are nested undera.**.(>10)all numbers anywhere in the JSON that are larger than10.{ a.b, d.e }object with keys matching their own query results.{ myKey: a[].b }keys can be renamed similarly like in JSON or JavaScript.[{ a[*].b, c.d }]list of objects expanded by values ofb.
When a key name in a path needs to be escaped, it can be done like a[b.c].d or a['b[]'].c.
Expressions
(?)value isn'tundefined.(<0)value is smaller than0.(a >= 5)whereais larger or equal to 5.(a ~= 'foo' && a != ['foo', 'foobar'])whereacontains"foo"and isn't"foo"nor"foobar".(status? && (status = [401, 403] || status >= 500))parenthesis can be used, too.
Pipe modifiers
a[ # | size ].bamount of uniquebvalues withinalist.a[|> max].blargestbvalue.a.b.c |> split._.7split valuecby underscore_, and return its 8th part.a[ |> sort.reverse[0] ]sort in descending order and take the first value.min,,sum,floor,ceil,round,trunc,avg, also supported.
Examples
Return a list of objects from a Babel AST of the test file.
program.body[]
.expression(callee.name = 'describe')
.arguments[](type = 'ArrowFunctionExpression')
.body.body[](
expression.callee.name = 'it' &&
expression.arguments[0].type = [
'TemplateLiteral',
'StringLiteral',
]
)
.expression.arguments{
path: [0].value,
path: [0].quasis[0].value.cooked,
vars: [1].body.body[](type = 'VariableDeclaration')
.declarations[0].id.name,
}Which returns something like this from Babel parsed test/setter.js file:
[
...
{ path: 'a.b', vars: [ 'out' ] },
{ path: 'a.b.c', vars: [ 'object', 'out' ] },
{ path: 'a[0]', vars: [ 'object', 'out' ] },
...
]Syntax allows us to go through step by step the JSON file, filtering further as we go more nested. Filter conditions always get the current value of list or object as the context, which reduces repeated syntax.
Another example, where a nested list of objects is expanded into a simpler list of objects:
[{
bb[*].dd,
bb[].ee[*].dd: bb[].ee[*].dd (!='dd13'),
bb[].ee[].hh[].ii.dd,
bb[].ee[].hh[].dd,
bb[].cc,
aa,
}]Returns list of objects separately for each value found with paths having [*] list expansion in them. Values above of the expanded paths are duplicated, while values for even further nested paths are aggregated.
[
...
{
'aa': 'aa1',
'bb[0].dd': 'dd1',
'bb[0].ee[0].dd': 'dd7',
'bb[0].ee[].hh[].dd': [ 'dd8', 'dd10' ],
'bb[0].ee[].hh[].ii.dd': [ 'dd9', 'dd11' ]
'bb[0].cc': 'cc1',
},
...
]Other methods
JSONway exposes following methods:
{
analyze,
calculateExpression,
expand,
expandAll,
flatten,
get,
has,
parse,
parseExpression,
set,
stringify
}For further explanations check documentation folder. For more examples check the test folder.
Why (yet another) JSON query language?
It's true that there are already many popular JSON query languages like JSONPath, JSONata, and JMESPath. I wanted to create my own syntax and language from scratch that was slightly different, while indeed inspired by those and many others.
Goals and guiding principles for JSONway have been to have concise but safety-restricted syntax, imitating JavaScript syntax and logic when possible, and zero dependencies. Creating table-like lists of objects from complex JSON structures, has been one of the main use cases JSONway has been trying to solve.
Writing your own parser and interpreter for your own query language are educational already on their own, and I would strongly recommend others to do the same. Deciding on a syntax and available capabilities for the language depends heavily on the use cases you have in mind, and I don't believe we will have "one query language to rule them all" in the future, either.
Hopefully JSONway can be the query language for your needs!
MIT licensed.
