jo: Build JSON From the Shell
jo turns key=value arguments into a JSON object, inferring numbers and booleans, so you can build a request body or payload without hand-writing brackets and quotes.
jo is jq run backwards: instead of querying JSON it produces it. For webhook payloads and API bodies in shell scripts, it beats fragile printf-with-escapes.
What it does
jo reads key=value pairs and emits a JSON object. It auto-types bare numbers and true/false/null; key=@file reads a value from a file; -a makes a top-level array; nesting is done by piping a jo array/object into a key=$(...) with -d for dotted keys. := is not jo syntax; use = and let inference or -s/-n control typing.
Common usage
jo name=Ada active=true age=36
# -> {"name":"Ada","active":true,"age":36}
jo -a 1 2 3 # -> [1,2,3]
jo user=$(jo name=Ada id=7) ok=true # nested object
jo -p msg="hello world" count=5 # -p pretty-prints
http POST api/users $(jo name=Ada role=admin) # build a bodyOptions
| Flag / form | What it does |
|---|---|
| key=value | Add a field; numbers/bools/null are auto-typed |
| -a | Produce a JSON array from the bare values |
| -p | Pretty-print with indentation |
| key=@file | Use the file contents as the value |
| key=%file | Base64-encode the file as the value |
| -s | Force every value to be a string (disable type inference) |
| -n | Drop keys whose value is an empty string |
In CI
jo guarantees valid JSON and proper escaping, which is exactly what hand-built strings get wrong when a value contains a quote or space. Quote the whole value when it has spaces (msg="hello world"). Use -s when a value that looks numeric (a zip code, a version) must stay a string.
Common errors in CI
"jo: command not found" (install via apt-get install -y jo, brew install jo, or build from source). A value with spaces splitting into multiple args means it was unquoted; wrap it in quotes. A leading-zero or version string becoming a number (or an error) means type inference kicked in; force a string with -s or by quoting per the jo docs. Nested objects need a sub-jo, not literal braces.