json
- JSON love for your command line
something-generating-JSON-on-stdout | json
[OPTIONS] [LOOKUPS]
json
-f FILE [OPTIONS] [LOOKUPS...]
json
is a fast command-line tool for working with JSON content from the
command line. Among its features: streaming stdin/stdout or working with JSON
files, pretty-printing with control over output formats, JSON validation,
filtering, modification, in-place JSON file modification, field extraction,
tabular output, skipping HTTP header blocks for use with REST API responses,
JSON stream ('\n'-separated JSON objects) processing.
Read on for details and examples. The FEATURE sections describe json
features roughly in the order of processing.
json
roots are as a tool to assist working with REST APIs. Often results
being parsed include HTTP headers, as from curl -i
, with a JSON payload.
By default json
passes through HTTP header blocks. Use -H
to strip a
leading HTTP header block.
(Added in json v4.) Use '-g' or '--group' to group adjacent objects into a single JSON array or to concatenate adjacent arrays into a single array. E.g.:
$ echo '{"a":1}
{"b": 2}' | json -g
[
{
"a": 1
},
{
"b": 2
}
]
$ echo '["one"]
["two"]' | json -g
[
"one",
"two"
]
"Adjacent" objects means objects separated by a newline, or by no space at all. Adjacent arrays means separate by a newline. These conditions are chosen as a balance between (a) not being ambiguous to parse with a simple regex and (b) enough to be useful for common cases.
Compatibility note: In json v3 and earlier, this used to be called "auto-arrayification" and was implicit. In json v4 and v5 grouping of adjacent arrays separated by no space was allowed. That was dropped in v6 (see issue #55). See the COMPATIBILITY section below.
Grouping can be helpful for "one JSON object per line" formats or for things such as:
$ cat *.json | json -g ...
However, when the size of the input is large practically one must do stream
processing. As of json v5.1, json -ga
will stream. An extreme example is:
$ yes '{"foo":"bar"}' | json -ga
But a more practical example would be a large file of newline-separated JSON objects, such as a Bunyan log file:
$ cat foo.log | json -ga req.method req.url res.headers.x-response-time
GET /ping 1
POST /images 43
...
(Added in json v4.) Use '--merge' or '--deep-merge' to merge adjacent JSON objects in the input. Keys in the last object win.
$ echo '{"one":"un","two":"deux"}
{"one":"uno","three":"tres"}' | json --merge
{
"one": "uno",
"two": "deux",
"three": "tres"
}
This could be useful for merging multiple config files, e.g.:
$ cat /opt/app/etc/defaults.json \
/etc/app/config.json \
~/.app/config.json | json --merge
...
(Added in json v9.) Looking up fields in an array of object is easy with json
:
$ echo '[{"name":"trent","age":38},
{"name":"ewan","age":4}]' | json -a name age
trent 38
ewan 4
but less so when a set of objects is indexed by key in an object:
$ echo '{"trent":{"age":38},
"ewan": {"age":4}}' | ... # How to list ages?
The -M, --items
option allows one to itemize the key/value pairs of
an object for convenient iteration with -a
:
$ echo '{"trent":{"age":38},
"ewan": {"age":4}}' | json -M
[
{
"key": "trent",
"value": {
"age": 38
}
},
{
"key": "ewan",
"value": {
"age": 4
}
}
]
$ echo '{"trent":{"age":38},
"ewan": {"age":4}}' | json -Ma key value.age
trent 38
ewan 4
# List people that can vote.
$ echo '{"trent":{"age":38},
"ewan": {"age":4}}' | json -M -c 'this.value.age > 18' -a key
trent
json
will give position information and context for JSON syntax errors
(SyntaxError
). This can be handy for validating data and config files:
$ cat config.json | json
json: error: input is not JSON: Unexpected ',' at line 17, column 5:
, { "name": "smartos64-1.4.7"
....^
{
"use-proxy": false
...
$ echo $?
1
Processing and output of the input JSON can be suppressed with the
-n, --validate
option:
$ cat config.json | json --validate
json: error: input is not JSON: Unexpected ',' at line 17, column 5:
, { "name": "smartos64-1.4.7"
....^
Together with the -q
you can get silent, exit-status-only, JSON validation:
$ cat config.json | json -nq
$ echo $?
1
Use the -e CODE
option to execute (JavaScript) code on the input JSON.
$ echo '{"name":"trent","age":38}' | json -e 'this.age++'
{
"name": "trent",
"age": 39
}
If input is an array, this will automatically process each item separately:
$ echo '[{"age":38},{"age":4}]' | json -e this.age++
[
{
"age": 39
},
{
"age": 5
}
]
That can be overriden with -A
:
$ echo '[{"age":38},{"age":4}]' | json -A -e 'this[0].age = "unknown"'
[
{
"age": "unknown"
},
{
"age": 4
}
]
The given CODE is executed in a function bound to the input object (i.e.
this
is the input object).
Security note: CODE
is not executed in a sandbox, so json
's globals are
available and unguarded. You can shoot yourself in the foot. Do not pass
untrusted user-supplied strings here.
Compatibility note: In versions before v9 -e CODE
used an alternate
implementation (with slightly different semantics for the CODE). It is still
supported for backward compatibility by using the JSON_EXEC=vm
environment
variable. However it is deprecated because it can cause processing to be 10x
or more slower for large inputs. See the COMPATIBILITY section below.
Use the -c CODE
option to run JavaScript code ending with a statement
returning a boolean to filter the input JSON.
$ echo '[{"age":38},{"age":4}]' | json -c 'this.age > 21'
[{"age":38}]
As with -e
above, if input is an array, this will automatically process each
item separately. This can be overriden with -A
.
The given CODE is executed in a function bound to the input object (i.e.
this
is the input object). A JavaScript function must use return
to return
a value, so as a convenience if "return" is not in the given CODE it is presumed
to be a single statement and it is wrapped:
function () {
return ( CODE );
}
To use multiple statements in -c CODE
you must explicitly use return
, e.g.:
$ echo '{"a": 2, "b": 6}' | json -c 'sum = this.a + this.b; return sum > 5'
{
"a": 2,
"b": 6
}
Security note: CODE
is not executed in a sandbox, so json
's globals are
available and unguarded. You can shoot yourself in the foot. Do not pass
untrusted user-supplied strings here.
Compatibility note: In versions before v9 -c CODE
used an alternate
implementation (with slightly different semantics for the CODE). It is still
supported for backward compatibility by using the JSON_EXEC=vm
environment
variable. However it is deprecated because it can cause processing to be 10x
or more slower for large inputs. See the COMPATIBILITY section below.
Use lookup arguments to extract particular values:
$ echo '{"name":"trent","age":38}' | json name
trent
Use '.' in a single lookup to do property access (e.g. name.first
) and use
multiple args to lookup multiple fields.
$ echo '{"name": {"first": "Trent", "last": "Mick"}, "age": 38}' \
| json name.first age
Trent
38
Use -a
for Array processing of lookups and tAbular output:
$ echo '{"name":"trent","age":38}' | json name
trent
$ echo '[{"name":"trent","age":38},
{"name":"ewan","age":4}]' | json -a name age
trent 38
ewan 4
Integral values work for array index lookups:
$ echo '["a", "b", "c"]' | json 1
b
Negative array indices are also supported for convenience (a la Python array indexing):
$ echo '["a", "b", "c"]' | json -- -1
c
$ echo '["a", "b", "c"]' | json -- -2
b
If your lookup isn't a number or a JS indentifier you can always use JavaScript array-style lookups like this:
$ echo '{"http://example.com": "my-value"}' | json '["http://example.com"]'
my-value
just like you would in JavaScript:
$ node
> var d = {"http://example.com": "my-value"}
> d["http://example.com"]
'my-value'
Or it might be easier to set an alternate lookup delimiter:
$ echo '{"http://example.com": "my-value"}' | json -D, http://example.com
my-value
$ echo '{"etc": {"resolv.conf":1, "passwd":2}}' | json -D/ etc/resolv.conf
1
Output is "jsony" by default: 2-space indented JSON ...
$ echo '{"name": "trent", "age": 38}' | json
{
"name": "trent",
"age": 38
}
... with one exception, a bare string value is printed without quotes (because who wants to deal with quotes in your pipeline?).
$ echo '{"name": "trent", "age": 38}' | json name
trent
If pure JSON output is wanted, use -o json
or the -j
shortcut:
$ echo '{"name": "trent", "age": 38}' | json -o json name
"trent"
Indentations other than 2 can be selected via -o json-N
$ echo '{"name": "trent", "age": 38}' | json -o json-0
{"name":"trent","age":38}
$ echo '{"name": "trent", "age": 38}' | json -o json-4
{
"name": "trent",
"age": 38
}
The "FORMAT-N" suffix can also be useful on "jsony" when selecting multiple values and wanting tabular output where some cells are objects:
$ cat users.json
[
{"name": {"first": "Trent", "last": "Mick"}, "age": 38},
{"name": {"first": "Ewan", "last": "Mick"}, "age": 4}
]
$ json -f users.json -a name age -o jsony-0
{"first":"Trent","last":"Mick"} 38
{"first":"Ewan","last":"Mick"} 4
Further the -0
, -2
, and -4
shortcuts can be used to set the indentation
without changing the mode. This can be use to make the above shorter:
$ json -f users.json -a name age -0
{"first":"Trent","last":"Mick"} 38
{"first":"Ewan","last":"Mick"} 4
You can get colored (non-JSON) output using node.js's
util.inspect
:
$ echo '[{"name": "Trent"},{"name": "Ewan"}]' | json -o inspect
[ { name: 'Trent' },
{ name: 'Ewan' } ]
Sometimes you want the list of keys for an object. Use -k
or --keys
for
that:
$ echo '{"name": "trent", "age": 38}' | json -k
[
"name",
"age"
]
$ echo '{"name": "trent", "age": 38}' | json -ka
name
age
You can edit a file in place with -I
and -f FILE
:
$ cat config.json
{"hostname":"127.0.0.1"}
$ json -I -f config.json # format the file
json: updated "config.json" in-place
$ cat config.json
{
"hostname": "127.0.0.1"
}
$ json -I -f config.json -e 'this.port=8080' # add port field
json: updated "config.json" in-place
$ cat config.json
{
"hostname": "127.0.0.1",
"port": 8080
}
Some limitations. Only one file at a time:
$ json -I -f foo.json -f bar.json
json: error: must specify exactly one file with '-f FILE' to use -I/--in-place
Lookups are not allowed:
$ json -I -f foo.json key.subkey
json: error: lookups cannot be specified with in-place editing (-I/--in-place), too easy to lose content
because that can too easily result in data loss, e.g. with something like:
$ json -I -f *.json # if there is more than one match to the glob
json: error: lookups cannot be specified with in-place editing (-I/--in-place), too easy to lose content
-h
, --help
Print this help info and exit.
--version
Print version of this command and exit.
-q, --quiet
Don't warn if input isn't valid JSON.
By default json
will process input from stdin. Alternatively, an input file
(or files) can be specified:
-f FILE
By default json
output is to stdout. Together with -f FILE
, in-place
editing can be done:
-I
, --in-place
-f FILE
in-place. Lookups are not allowed
with in-place editing, because it is too easy to accidentally lose file
data.If your JSON output is a REST API response, it might include the headers
(e.g. when calling with curl -i
). By default json
will pass those headers
through (without choking on them). However if you want them stripped you
can use:
-H
curl -i ...
)Other pre-JSON input handling:
-g
, --group
Group adjacent objects into an array of objects, or concatenate adjacent arrays into a single array.
--merge
, --deep-merge
Merge adjacent objects into a single object with merged keys. Values
in later objects win. Use --deep-merge
to recursively merge keys in
objects.
-M
, --items
Itemize an object into an array of {"key": <key>, "value": <value>}
objects for easier processing.
You can process elements of an input array separately and generate tabular output:
-a
, --array
Process input as an array of separate inputs and output in tabular form.
-d DELIM
Delimiter character for tabular output (default is ' '). This supports JSON escapes, e.g.: '\t' for tab or '\u0000' for the null byte.
-A
Process input as a single object, i.e. stop -e
and -c
automatically
processing each item of an input array.
You can execute code on (-e
) and filter (-c
) the input (this is done before
LOOKUPS are processed, if any).
-e CODE
Execute the given JavaScript code on the input. If input is an array, then
each item of the array is processed separately (use -A
to override). Use
this.
to access fields on the object being processed. (-E CODE
is a now
deprecated synonym.)
-c CODE
Filter the input with JavaScript CODE
. If CODE
returns false-y, then
the item is filtered out. If input is an array, then each item of the array
is processed separately (use -A
to override). Use this.
to access fields
on the object being processed. An explicit return
is required if CODE
includes multiple statements. (-C CODE
is a now deprecated synonym.)
Finally, if LOOKUP
arguments are given, these are extracted from the
JSON. By default .
is used as a separator for nested object lookup.
This can be overridden:
-D DELIM
$ echo '{"a.b": {"b": 1}}' | json -D / a.b/b
An alternative to lookups is to output the keys of the input object:
-k
, --keys
json
can be restricting to just validating its input, i.e. processing
and output of the input is skipped:
-n
, --validate
By default json
outputs in "jsony" mode. Basically this is JSON output,
with the exception that a single string output value is emitted without the
quotes. The intention here is to be of most use to the UNIX command-line.
Other output formats are supported:
-o MODE
, --output MODE
Specify an output mode. One of jsony
(the default; JSON, if a single
string then quotes are elided), json
(JSON output, 2-space indent),
or inspect
(node.js util.inspect
output). json
and jsony
modes can
specify an indentation via json-N
or jsony-N
for N-space indentation
(e.g. json-4
), or via json-tab
or jsony-tab
for TAB indentation.
-i
Shortcut for -o inspect
.
-j
Shortcut for -o json
.
-0
, -2
, -4
Set the JSON indentation without changing the mode.
JSON_EXEC=vm
-e CODE
and -c CODE
.A typical JSON REST API response:
$ curl -s http://ifconfig.me/all.json
{"connection":"","ip_addr":"216.57.203.67","lang":"","remote_host":...
Nice output by default:
$ curl -s http://ifconfig.me/all.json | json
{
"connection": "",
"ip_addr": "201.73.103.12",
"lang": "",
"remote_host": "",
"user_agent": "curl/7.23.1 (i386-sun-solaris2.11) libcurl/7.23.1 OpenSSL/0.9.8w zlib/1.2.3 libidn/1.23 libssh2/1.2.2",
"charset": "",
"port": "63713",
"via": "",
"forwarded": "",
"mime": "*/*",
"keep_alive": "",
"encoding": ""
}
Say you just want to extract one value:
$ curl -s http://ifconfig.me/all.json | json ip_addr
201.73.103.12
Or, looking at the node.js project using the Github API:
$ curl -s https://api.github.com/repos/joyent/node | json open_issues
517
If you use curl -i
to get HTTP headers (because perhaps they contain
relevant information), json will skip the HTTP headers automatically:
$ curl -is https://api.github.com/repos/joyent/node | json
HTTP/1.1 200 OK
Server: nginx/1.0.13
Date: Tue, 24 Jul 2012 04:01:08 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Status: 200 OK
ETag: "1a21d980a01768dde42145ce2b58694c"
X-RateLimit-Remaining: 4997
Content-Length: 1513
Cache-Control: public, max-age=60
Vary: Accept
X-RateLimit-Limit: 5000
Last-Modified: Tue, 24 Jul 2012 03:50:11 GMT
{
"master_branch": "master",
"has_issues": true,
"has_downloads": false,
"homepage": "http://nodejs.org/",
"html_url": "https://github.com/joyent/node",
...
Or, say you are stuck with the headers in your pipeline, 'json -H' will drop HTTP headers:
$ curl -is https://api.github.com/repos/joyent/node | json -H forks
2158
Here is an example that shows indexing a list. (The given "lookup" argument is basically JavaScript code appended, with '.' if necessary, to the JSON data and eval'd.)
$ curl -s https://api.github.com/legacy/repos/search/nodejs \
| json 'repositories[2].name'
socket.io
Having the quote to avoid shell interpretation of '[' is annoying, so json
allows a special case for an integer lookup:
$ curl -s https://api.github.com/legacy/repos/search/nodejs \
| json 'repositories.2.name'
socket.io
json
includes the -a
(aka --array
) option for processing each element of
an input JSON array independently and using tabular output. Let's first
get a list of open node.js issues (note that this is a subset because of
GH API pagination):
$ curl -s https://api.github.com/repos/joyent/node/issues?state=open\&per_page=100
[
{
"number": 3757,
"html_url": "https://github.com/joyent/node/issues/3757",
"body": "Fix #3756.\n\nReview, please: @TooTallNate",
"milestone": null,
"user": {
"gravatar_id": "73a2b24daecb976af81e010b7a3ce3c6",
"login": "isaacs",
"avatar_url": "https://secure.gravatar.com/avatar/73a2b24dae...
...
We can then print a table with just some fields as follows:
$ curl -s https://api.github.com/repos/joyent/node/issues?state=open\&per_page=100 \
| json -a comments number title
0 3757 readline: Remove event listeners on close
0 3756 readline: No way to completely unhook interface from input/output
1 3755 node-v0.6.20 hello example segfaults on RaspberryPi (w/Arch + bash)
0 3753 Prohibit same listeners in EventEmitter. Closes #964.
1 3752 Auto-detect hardfloat eabi and armv7 variables on ARM based on compiler
3 3751 persistent REPL history
0 3749 glibc errors on SheevaPlug / Debian Squeeze
...
Ultimately this can be useful for then using other command-line tools. For example, we could get the list of top-five most commented open node issues:
$ curl -s https://api.github.com/repos/joyent/node/issues?state=open\&per_page=100 \
| json -a comments number title | sort -n | tail -5
9 3510 Automatically `.toString()` functions in REPL.
11 3668 JSON documentation index listing
12 3624 Add a return value to Buffer.write* methods that returns the ...
12 3655 defer dgram listening event
14 3613 Connections closed by node stay permanently in FIN_WAIT2
Or get a breakdown by ISO language code of the recent tweets mentioning "nodejs":
$ curl -s http://search.twitter.com/search.json?q=nodejs\&rpp=100 \
| json results | json -a iso_language_code | sort | uniq -c | sort
1 es
1 no
1 th
4 ru
12 ja
23 pt
58 en
The -d
option can be used to specify a delimiter:
$ curl -s https://api.github.com/repos/joyent/node/issues?state=open \
| json -a created_at number title -d,
2012-07-24T03:45:03Z,3757,readline: Remove event listeners on close
2012-07-24T03:32:10Z,3756,readline: No way to completely unhook inte...
2012-07-23T21:17:50Z,3755,node-v0.6.20 hello example segfaults on Ra...
2012-07-22T16:17:49Z,3753,Prohibit same listeners in EventEmitter. C...
2012-07-22T13:43:40Z,3752,Auto-detect hardfloat eabi and armv7 varia...
The json tool major version is incremented when there is a backward incompatible change. An overview of those changes is here.
-e CODE
and -c CODE
switch away from using vm.runInNewContext
for
processing. In other words they now do what -E
and -C
do, and the
uppercase options are not deprecated synonyms. Use the JSON_EXEC=vm
environment variable to bring back the old behaviour.-E CODE
and -C CODE
were added in favour of -e CODE
and -c CODE
because the former provide a 10x or more performance improvement for
larger inputs. The latter are still included for backward compatibility.
-E/-C
use a JavaScript function to execute CODE, which -e/-c
use node.js's
vm.runInNewContext
which is crazy slow. Use of a JavaScript function
places slightly different semantics and requirements on the given CODE
, so
new options were required for compat.-g
or --group
) of adjacent arrays no longer groups
arrays separated by no space. I.e. adjacent arrays must be separated by a
newline.-j
or -o json*
) to only output the value instead of the more general array or
table that is necessary for multiple lookups.See the changelog for full compatibility and change details.
json
is written in JavaScript and requires node.js (node
). You can either
install via npm
:
npm install -g json
or manually get the script and put it on your PATH somewhere (json
is a single
file with no external deps other than node itself):
cd ~/bin
curl -L https://github.com/trentm/json/raw/master/lib/json.js > json
chmod 755 json
(Note: Before version 8, this tool was called "jsontool" in the npm registry. That name is now defunct.)
This project lives at https://github.com/trentm/json. Please report bugs to https://github.com/trentm/json/issues. See the full changelog at: https://github.com/trentm/json/blob/master/CHANGES.md.
MIT License (see https://github.com/trentm/json/blob/master/LICENSE.txt)
json is Copyright 2021 Trent Mick and Copyright 2020 Joyent Inc.