JT(1)

๐Ÿ“ฆ version 4.3.3, in opensuse-leap 16.0

okay so bestie, welcome to jt(1) from the JT MANUAL โ€” the page for jt, the tool that transforms JSON data into tabular format. Buckle up, because this is stack-based and kinda iconic ๐Ÿ’…โœจ

๐ŸŒธ NAME ๐ŸŒธ

jt โ€” transform JSON data to tabular format. That's literally the whole assignment: JSON in, table out ๐Ÿซถ

๐ŸŽ€ SYNOPSIS ๐ŸŽ€

jt [-hV]
jt -u string
jt [-acj] [COMMAND ...]

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

jt reads UTF-8 encoded JSON forms from stdin and writes tab separated values (or CSV) to stdout. A simple stack-based programming language is used to extract values from the JSON input for printing. So yeah: JSON comes in, your little command stack does its thing, and a table comes out. No cap ๐Ÿ’–

โœจ OPTIONS โœจ

  • -h โ€” Print usage info and exit.
  • -V โ€” Print version and license info and exit.
  • -a โ€” Explicit iteration mode: require the . command to iterate over arrays instead of iterating automatically.
  • -c โ€” CSV output mode: write RFC 4180 compliant CSV records.
  • -j โ€” Inner join mode: discard rows with missing columns.
  • -s โ€” A no-op, included for compatibility with earlier versions.
  • -u string โ€” Unescape JSON string, print it, and exit. Double-quotes around string are optional.
-s is literally a no-op, bestie โ€” it's there for compatibility with earlier versions, not because it does anything.

๐Ÿ’… OPERATION ๐Ÿ’…

Non-option arguments are words (commands) in a stack-based programming language. Commands operate on the stacks provided by the jt runtime:

  • The DATA stack contains JSON values commands will operate on.
  • The GOSUB stack contains pointers into the data stack.
  • The INDEX stack contains pointers to iterators during iteration.
  • The LOOP stack contains pointers to iterators between iterations.
  • The OUTPUT stack contains values to print after each iteration.

When jt starts, the following happens:

  1. One JSON form is read from stdin, parsed, and pushed onto the data stack.
  2. If the top of the data stack is a JSON array the next item in the array is pushed onto the data stack based on state previously saved on the loop stack. The loop stack is then popped and pushed onto the index stack.
  3. The next command is executed.
  4. Steps 2 and 3 are repeated until no commands are left to execute.
  5. Values in the output stack are printed, separated by tabs and followed by a newline (see OUTPUT FORMAT below). Data, output, and gosub stacks are reset to their states as they were after step 1.
  6. Loop variables are popped off of the index stack. They are then incremented and pushed onto the loop stack as necessary.
  7. Steps 2 through 6 are repeated until the loop stack is empty (i.e. there are no more iterations left).

This process is repeated until there is no more JSON to read.

๐Ÿ”ฅ COMMANDS ๐Ÿ”ฅ

jt provides the following commands:

  • [ โ€” Save the state of the data stack: the current data stack pointer is pushed onto the gosub stack.
  • ] โ€” Restore the data stack to a saved state: pop the gosub stack and restore the data stack pointer to that state.
  • % โ€” Push the value at the top of the data stack onto the output stack.
  • %=NAME โ€” Like %, but also sets the heading for this column to NAME. Headers will be printed as the first line of output when this command has been used.
  • ^ โ€” Push the value at the top of the index stack onto the output stack. Note that the index stack will always have at least one item: the index of the last JSON object read from stdin.
  • ^=NAME โ€” Like ^, but also sets the heading for this column to NAME. Headers will be printed as the first line of output when this command has been used.
  • @ โ€” Print the keys of the object at the top of the data stack and exit.
    • If the value at the top of the data stack is an array the first item in the array is pushed onto the data stack and the command is reevaluated.
    • If the value at the top of the data stack is not an object or an array then its type is printed in brackets, eg. [array], [string], [number], [boolean], or [null]. If there is no value then [none] is printed.
  • + โ€” Parse embedded JSON: if the item at the top of the data stack is a string, parse it and push the result onto the data stack.
    • If the item at the top of the data stack is not a string or if there is an error parsing the JSON then nothing is done (the operation is a no-op).
  • . โ€” Iterate over the values of the object at the top of the data stack. The current value will be pushed onto the data stack and the current key will be pushed onto the index stack.
  • [KEY] โ€” Drill down: get the value of the KEY property of the object at the top of the data stack and push that value onto the data stack.
    • If the item at the top of the data stack is not an object or if the object has no KEY property a blank field is printed, unless the -j option was specified in which case the entire row is removed from the output.
    • If the KEY property of the object is an array subsequent commands will operate on one of the items in the array, chosen automatically by jt. The array index will be available to subsequent commands via the index stack.
  • KEY โ€” See [KEY] above โ€” the [ and ] may be omitted if the property name KEY does not conflict with any jt command.

๐ŸŒˆ OUTPUT FORMAT ๐ŸŒˆ

The format of printed output from jt (see % in COMMANDS, above) depends on the type of thing being printed:

๐Ÿ“ Primitives ๐Ÿ“

JSON primitives (i.e. null, true, and false) and numbers are printed verbatim. jt does not process them in any way. Numbers can be of arbitrary precision, as long as they conform to the JSON parsing grammar.

If special formatting is required the printf program is your friend:

$ printf %.0f 2.99792458e9
2997924580

๐ŸŽ€ Strings ๐ŸŽ€

Strings are printed verbatim, minus the enclosing double quotes. No unescaping is performed because tabs or newlines in JSON strings would break the tabular output format.

If unescaped values are desired either use the -c flag to enable CSV output (see CSV below) or use the -u option and pass the string as an argument:

$ jt -u ยดi love music \u266Aยด
i love music โ™ช

๐Ÿฆ‹ Collections ๐Ÿฆ‹

Objects and arrays are printed as JSON with whitespace removed. Note that it is only possible to print arrays when the -a option is specified (see the Iteration (Arrays) and Explicit Iteration sections below).

๐Ÿ’œ CSV ๐Ÿ’œ

The -c flag enables CSV output conforming to RFC 4180. This format supports strings containing tabs, newlines, etc.:

$ jt -c [ foo % ] [ bar % ] baz % <<EOT
- {
-   "foo": "one\ttwo",
-   "bar": "three\nfour",
-   "baz": "music \"\u266A\""
- }
- EOT
"one    two","three
four","music ""โ™ช"""

๐Ÿ‘‘ EXIT STATUS ๐Ÿ‘‘

jt will exit with a status of 1 if an error occurred, or 0 otherwise.

๐ŸŒธ EXAMPLES ๐ŸŒธ

okay bestie this is the fun part โ€” a whole bunch of tiny worked examples showing you how to poke around JSON and JSON streams with jt and actually pull stuff out. explore, extract, iterate, joinโ€ฆ it's giving a whole curriculum fr fr ๐Ÿ’…


๐Ÿฆ‹ Explore

The @ command prints info about whatever's sitting on top of the data stack. When that thing is an object, @ spills out its keys:

$ jt @ <<EOT
- {
-   "foo": 100,
-   "bar": 200,
-   "baz": 300
- }
- EOT
foo
bar
baz

When the top item is an array, @ gives you info about the first item in the array:

$ jt @ <<EOT
- [
-   {"foo": 100, "bar": 200},
-   {"baz": 300, "baf": 400}
- ]
- EOT
foo
bar

Otherwise @ just prints the type of the item โ€” or [none] if there's literally no value at all:

$ echo ยด["hello", "world"]ยด | jt @
[string]

$ echo ยด[1, 2]ยด | jt @
[number]

$ echo ยด[true, false]ยด | jt @
[boolean]

$ echo ยด[null, null]ยด | jt @
[null]

$ echo ยด[]ยด | jt @
[none]

๐Ÿซถ Drill Down

Property names are also commands, iconic behaviour honestly. Use foo as a command to drill down into the foo property, then @ to print its keys:

$ jt foo @ <<EOT
- {
-   "foo": {
-     "bar": 100,
-     "baz": 200
-   }
- }
- EOT
bar
baz

Now, if a property name happens to clash with one of jt's own commands, just wrap it in square brackets to drill down anyway:

$ jt [@] @ <<EOT
- {
-   "@": {
-     "bar": 100,
-     "baz": 200
-   }
- }
- EOT
bar
baz
๐Ÿ’ก Note: the property name has to be a JSON escaped string. So if the property name in your JSON input contains something like tab characters, you gotta escape them in the command too, like this:
$ jt ยดfoo\tbarยด @ <<EOT
- {
-   "foo\tbar": {
-     "bar": 100,
-     "baz": 200
-   }
- }
- EOT
bar
baz

โœจ Extract

The % command prints the item on top of the data stack. Heads up though โ€” when the top item is a collection, it gets printed as JSON (with that insiginificant whitespace removed):

$ jt % <<EOT
- {
-   "foo": 100,
-   "bar": 200
- }
- EOT
{"foo":100,"bar":200}

Drill down, then print:

$ jt foo bar % <<EOT
- {
-   "foo": {
-     "bar": 100
-   }
- }
- EOT
100

And you can absolutely use % more than once โ€” the printed values come out separated by tabs:

$ jt % foo % bar % <<EOT
- {
-   "foo": {
-     "bar": 100
-   }
- }
- EOT
{"foo":{"bar":100}}     {"bar":100}     100

๐ŸŽ€ Save / Restore

The [ and ] commands are your little save points ๐ŸŽฎ โ€” they let you stash the data stack's state before doing some operations, then restore it afterwards. Which means you can drill down into one part of an object, "rewind", and then drill down into a whole other part of the same object. main character energy tbh.

[ saves the state of the data stack, and ] restores the data stack back to the matching saved state.

โš ๏ธ It is an error to use the ] command without a corresponding [ command. no cap, don't do it.
$ jt [ foo % ] bar % <<EOT
- {
-   "foo": 100,
-   "bar": 200
- }
- EOT
100     200

And yeah, [ and ] nest like a dream:

$ jt [ foo [ bar % ] [ baz % ] ] baf % <<EOT
- {
-   "foo": {
-     "bar": 100,
-     "baz": 200
-   },
-   "baf": "quux"
- }
- EOT
100     200     quux

๐ŸŒˆ Iteration (Arrays)

jt automatically iterates over arrays (unless you've disabled that behaviour โ€” see Explicit Iteration below ๐Ÿ‘€), producing one tab-delimited record per iteration, with records separated by newlines:

$ jt [ foo % ] bar baz % <<EOT
- {
-   "foo": 100,
-   "bar": [
-     {"baz": 200},
-     {"baz": 300},
-     {"baz": 400}
-   ]
- }
- EOT
100     200
100     300
100     400

The ^ command sneaks the array index in as a column of the result:

$ jt [ foo % ] bar ^ baz % <<EOT
- {
-   "foo": 100,
-   "bar": [
-     {"baz": 200},
-     {"baz": 300},
-     {"baz": 400}
-   ]
- }
- EOT
100     0       200
100     1       300
100     2       400

And ^ is scoped to the innermost enclosing loop โ€” so it knows exactly which loop it belongs to:

$ jt foo ^ bar ^ % <<EOT
- {
-   "foo": [
-     {"bar": [100, 200]},
-     {"bar": [300, 400]}
-   ]
- }
- EOT
0       0       100
0       1       200
1       0       300
1       1       400

๐Ÿ“ Iteration (Objects)

The . command iterates over the values of an object:

$ jt . % <<EOT
- {
-   "foo": 100,
-   "bar": 200,
-   "baz": 300
- }
- EOT
100
200
300

And when you're iterating over an object, ^ prints the name of the current property instead:

$ jt . ^ % <<EOT
- {
-   "foo": 100,
-   "bar": 200,
-   "baz": 300
- }
- EOT
foo     100
bar     200
baz     300

๐Ÿ’œ JSON Streams

jt automatically iterates over the entities in a JSON stream (optionally delimited by whitespace):

$ jt [ foo % ] bar % <<EOT
- {"foo": 100, "bar": 200}
- {"foo": 200, "bar": 300}
- {"foo": 300, "bar": 400}
- EOT
100     200
200     300
300     400

And ^ prints the current stream index:

$ jt ^ [ foo % ] bar % <<EOT
- {"foo": 100, "bar": 200}
- {"foo": 200, "bar": 300}
- {"foo": 300, "bar": 400}
- EOT
0       100     200
1       200     300
2       300     400

๐Ÿงš Nested JSON

The + command parses JSON that's been embedded inside strings โ€” yes, JSON in your JSON, we love a recursive moment:

$ jt [ foo + bar % ] baz % <<EOT
- {"foo":"{\"bar\":100}","baz":200}
- {"foo":"{\"bar\":200}","baz":300}
- {"foo":"{\"bar\":300}","baz":400}
- EOT
100     200
200     300
300     400
๐Ÿ’ก Note: the + command does not modify the original JSON:
$ jt [ foo + bar % ] % <<EOT
- {"foo":"{\"bar\":100}","baz":200}
- {"foo":"{\"bar\":200}","baz":300}
- {"foo":"{\"bar\":300}","baz":400}
- EOT
100     {"foo":"{\"bar\":100}","baz":200}
200     {"foo":"{\"bar\":200}","baz":300}
300     {"foo":"{\"bar\":300}","baz":400}

๐Ÿ‘‘ Column Headings

%=NAME and ^=NAME are exactly like % and ^ above, except they also assign column headings โ€” which get printed as the very first line of output. slay:

$ jt [ foo %=Foo ] bar ^=Bar baz %=Baz <<EOT
- {
-   "foo": 100,
-   "bar": [
-     {"baz": 200},
-     {"baz": 300},
-     {"baz": 400}
-   ]
- }
- EOT
Foo     Bar     Baz
100     0       200
100     1       300
100     2       400
๐Ÿ’ก Note: the heading NAME must be a JSON escaped string โ€” so if you want a column heading with embedded tab characters, use %=foo\tbar instead of "%=foo bar".

๐Ÿ”ฅ Joins

First, peep the empty column โ€” some of these objects just don't have the bar key, and that's fine:

$ jt [ foo % ] bar % <<EOT
- {"foo":100,"bar":1000}
- {"foo":200}
- {"foo":300,"bar":3000}
- EOT
100     1000
200
300     3000

Now turn on inner join mode with the -j flag. This drops output rows whenever a key in the traversal path doesn't exist:

$ jt -j [ foo % ] bar % <<EOT
- {"foo":100,"bar":1000}
- {"foo":200}
- {"foo":300,"bar":3000}
- EOT
100     1000
300     3000
๐Ÿ‘€ Note that this does not remove rows when the key exists and the value is just empty:
$ jt -j [ foo % ] bar % <<EOT
- {"foo":100,"bar":1000}
- {"foo":200,"bar":""}
- {"foo":300,"bar":3000}
- EOT
100     1000
200
300     3000

๐Ÿ’€ Explicit Iteration

Sometimes that implicit iteration over arrays is justโ€ฆ awkward. like, bestie, read the room.

$ jt . ^ . ^ % <<EOT
- {
-   "foo": [
-     {"bar":100},
-     {"bar":200}
-   ]
- }
- EOT
0       bar     100
1       bar     200

So the question is: should that first ^ be printing the array index of the implicit iteration (which, in this case, it is), or the object key (i.e. foo) of the explicit iteration of the . command? the ambiguity is loud ngl.

Another awkward case is printing arrays:

$ jt foo % <<EOT
- {
-   "foo": [
-     {"bar":100},
-     {"bar":200}
-   ]
- }
- EOT
{"bar":100}
{"bar":200}

The array can't be printed with the % command because it's being iterated over implicitly. Instead the items in the array get printed, which may not be what you actually wanted. ๐Ÿ™ˆ

The -a flag eliminates the ambiguity by enabling explicit iteration. In this mode you must use the . command to iterate over both objects and arrays โ€” arrays do not get automatically iterated over.

Now the array can be printed:

$ jt -a foo % <<EOT
- {
-   "foo": [
-     {"bar":100},
-     {"bar":200}
-   ]
- }
- EOT
[{"bar":100},{"bar":200}]

Or the first ^ can print the array index, same as before:

$ jt -a . . ^ . ^ % <<EOT
- {
-   "foo": [
-     {"bar":100},
-     {"bar":200}
-   ]
- }
- EOT
0       bar     100
1       bar     200

Or it can print the object key:

$ jt -a . ^ . . ^ % <<EOT
- {
-   "foo": [
-     {"bar":100},
-     {"bar":200}
-   ]
- }
- EOT
foo     bar     100
foo     bar     200

Or, with just one more ^ command added, it can print both:

$ jt -a . ^ . ^ . ^ % <<EOT
- {
-   "foo": [
-     {"bar":100},
-     {"bar":200}
-   ]
- }
- EOT
foo     0       bar     100
foo     1       bar     200

๐Ÿ’ซ SEE ALSO ๐Ÿ’ซ

jt is based on ideas from the excellent jshon tool, which you can find at http://kmkeen.com/jshon/. Source code for jt lives at https://github.com/micha/json-table. go be nosy, iykyk ๐Ÿซถ


๐Ÿ› BUGS ๐Ÿ›

Please open an issue right here: https://github.com/micha/json-table/issues. do it, the maintainers wanna hear from you ๐Ÿ’–

okay bestie, legal era ๐Ÿ’… this is the part where we give credit where credit is due, and honestly it's kind of a slay.

Copyright ยฉ 2017 Micha Niskin <micha.niskin@gmail.com>, distributed under the Eclipse Public License, version 1.0. ๐Ÿฆ‹

so like, this is free software โ€” and no, "free" here means freedom, not free-as-in-someone-bought-you-a-strawberry-matcha ๐Ÿ“ you are free to change it and redistribute it, go absolutely off, remix the whole thing, that's literally the point. periodt.

โš ๏ธ but hear me on this one: there is NO WARRANTY, to the extent permitted by law. like none. zero. nada. if it breaks, that's between you and the void ๐Ÿ˜ญ it's giving "no cap, you're on your own" โ€” but like, lovingly. ๐Ÿซถ

and that's a wrap on JT(1), bestie!! ๐ŸŽ€โœจ you read the whole thing, which honestly is main character energy of the highest order ๐Ÿ‘‘ this page is from July 2017, and the source/version line at the bottom literally just says JT(1) โ€” so yeah, that's the whole flex. go forth, run your commands, and remember: read the manpage first, slay always ๐Ÿ’–๐ŸŒˆโญ

๐Ÿ“ฆ where this page comes from