August 2011: Friendlier scripting.
Jshon parses, reads and creates JSON. It is designed to be as usable as possible from within the shell and replaces fragile adhoc parsers made from grep/sed/awk. Requires Jansson
- Download jshon.tar.gz
- Forum thread
- Github
-
Or install with
pacman -S jshon
It Jshon loads json text from stdin, performs actions, then dumps to stdout. Some of the options return json, others output meta information. Because Bash has very poor nested datastructures, Jshon does not try to return a native bash datastructure containing the json. Instead, Jshon provides a history stack containing all the manipulations.
ACTIONS
Each action takes the form of a short option. Some require arguments. All examples will work on this json sample:
{"a":1,"b":[true,false,null,"none"],"c":{"d":4,"e":5}}
jshon [actions] < sample.json
-t
(type) returns string, object, list, number, bool, null
jshon -t -> object
-l
(length) returns an integer. Only works on string, object, list.
jshon -l -> 3
-k
(keys) returns a newline separated list of keys. Only works on object.
jshon -k -> a b c
-e index
(extract) returns json value at "index". Only works on object, list.
jshon -e c -> {"d":4,"e":5}
-a
(across) maps the remaining actions across the selected element. Only works on objects and lists. Multiple -a calls can be nested, though the need is rare in practice.
jshon -e b -a -t -> bool bool null string
-s value
(string) returns a json encoded string. Ignores stdin.
jshon -s "back\\slash" -> "back\\\\slash"
-u
(unstring) returns a decoded string. Only works on string.
jshon -e b -e 3 -u -> none
## -p
(pop) pops the last manipulation from the stack, rewinding the history. Useful for extracting multiple values from one object.
jshon -e c -e d -u -p -e e -u -> 4 5
-m index,value
(modify) edits an element in a list or a dict. Value must be escaped. If value is remove, delete the index. If value is true/false/null, insert the primitive type. Quoting strings is optional, except for the ambiguous case of remove/true/false/null. Lists support several magic indexes. Negative numbers wrap around the back, and append will add a new element after the last.
jshon -m a,first -> {"a":"first", ...}
-i index
(insert) is complicated. It is the reverse of extract. Extract saves a copy of the json on a stack. Insert pops json from the stack, and inserts that bit of json into the new top of the stack. Use extract to dive into the json tree, modify to change something, and insert to push the changes back up the tree.
jshon -e a -i a -> the orginal json
jshon -e b -m 2,remove -m 2,remove -m append,1.2 -i b ->
{"a":1, "b":[true,false,1.2], "c":{"d":4,"e":5}}
## Non-manipulation
These are meta options that do not directly edit json. They are called at most once per invocation and are distinguished by capital letters.
## -P
(jsonp) strips a jsonp callback before continuing normally.
## -S
(sort) returns output sorted by key, instead of the original ordering.
## -Q
(quiet) disables error reporting on stderr, so you don't have to sprinkle 2> /dev/null
throughout your script.
REAL EXAMPLE
Let's say you are making a script to download backgrounds from the internet. Specifically, the pictures on EarthPorn. Reddit has a json interface:
curl http://www.reddit.com/r/earthporn.json
But now we need to extract the URLs out of there. I could fire up a text editor and dive into the json layout, but it is faster to explore the json using Jshon as a browser. If I find an object, I look at the keys. If an array, the first element.
$ jshon -t < earthporn.json
object
$ jshon -k < earthporn.json
kind
data
$ jshon -e data -t < earthporn.json
object
$ jshon -e data -k < earthporn.json
modhash
children
after
before
$ jshon -e data -e children -t < earthporn.json
array
$ jshon -e data -e children -e 0 -t < earthporn.json
object
$ jshon -e data -e children -e 0 -k < earthporn.json
kind
data
$ jshon -e data -e children -e 0 -e data -t < earthporn.json
object
$ jshon -e data -e children -e 0 -e data -k < earthporn.json
.... SNIP
url
....
$ jshon -e data -e children -e 0 -e data -e url -u < earthporn.json
http://i.imgur.com/.....
Cool, now we know where a single picture is in the hierarchy. (The -u on the end removes any json-safe encoding.) But how do we get all the pictures? Each url is under a different array index. So we use the -a
action to map across all the elements of the array.
jshon -e data -e children -a -e data -e url -u < earthporn.json
Yup, just simply replace the -e 0
and all the links are extracted. Of course, not all URLs are images so a quick regex cleans those up. The final script looks like
curl -s http://www.reddit.com/r/earthporn.json | \
jshon -e data -e children -a -e data -e url -u | \
grep '.[jpe|jp|pn]g$'
BUGS
Numerous! Forward slashes are not escaped, but that is a Jansson bug. Object indexes with commas are broken. On many errors it exits without saying why. Documentation is very brief. Memory use can be high for large and complex operations, but typically Jshon uses less memory than cat
.