I got tired of attempting to get shell scripts to produce valid JSON. You’ve likely seen something like this before:
echo '{"name":"Jane"}'
It gets merrier if an element contains an environment variable: open double, close single, add variable, open single, blergh.
A here! script will do it as will a a printf(1), but neither much improve legibility, and if strings contain quotes, it becomes almost impossible to make a script produce JSON.
printf '{"name": "%s"}\n' "Jane"
Enter jo:
$ jo name=Jane
{"name":"Jane"}
The idea occurred to me late at night, and I don’t know why this has taken so long to happen:
$ jo time=$(date +%s) dir=$HOME
{"time":1457195712,"dir":"/Users/jpm"}
Bam! Jo tries to be clever about types and knows null
, booleans, strings and numbers. It does arrays, and it pretty-prints on demand:
$ jo -p -a spring summer winter
[
"spring",
"summer",
"winter"
]
Inspired by a comment on HN, I added another hack: if a key’s value begins with an opening brace ({
) or a bracket ([
]) we attempt to decode JSON from it; this allows jo to add objects or arrays (use -a
!) to itself. Watch:
$ jo -p name=JP object=$(jo fruit=Orange point=$(jo x=10 y=20) number=17) sunday=false
{
"name": "JP",
"object": {
"fruit": "Orange",
"point": {
"x": 10,
"y": 20
},
"number": 17
},
"sunday": false
}
jo also supports nested types natively:
$ jo -p number=17 pass=true geo[lon]=88 geo[cc]=ES point[]=1 point[]=2 geo[lat]=123.45
{
"number": 17,
"pass": true,
"geo": {
"lon": 88,
"cc": "ES",
"lat": 123.45
},
"point": [
1,
2
]
}
Why did I do this? I need lots of JSON for testing OwnTracks, and this just looks better in scripts.
$ jo _type=location \
cog=$((RANDOM % 360)) \
t=u \
lat=48.85833 \
lon=2.29513 \
acc=5 \
tid=JJ \
tst=$(date +%s) | mosquitto_pub -t owntracks/jjolie/test -l
A suggestion by Andrew Bibby brought along the possibility of obtaining JSON element values from files, so I added @file
to read a file (sans traling newline or carriage return) into a value as well as something which I use a lot, convert binary files to base64, using the %file
syntax:
$ jo _type=card name="Vanessa" face=%vanessa.png
{"_type":"card","name":"Vanessa","face":"iVBORw0KGgoAAA ... QmCC"}
And it has a man page. Go get it. Jo!
Updates:
- Caius Durling provided a Homebrew tap for jo, but
jo
is in Homebrew Formulae now, sobrew install jo
. - jo has a logo which it created itself; colors inspired by something Frederic Cambus said :-)
- Shawn Webb added jo to HardenedBSD’s ports.
- I’ve converted jo to use autotools; releases will be published here.
- Alessio Sergi has added jo to voidlinux; install with
xbps-install -Su jo
- I’ve built a Win32 binary which you can get from releases/.
- Lance Chen has an ArchLinux port of jo.
- Vincent Bernat has submitted jo to Debian.