select values out of nested JSON, YAML and XML documents

Examples

Every command below is copied from a real terminal, together with the output it produced. Nothing here needs a configuration file or a plugin.

A value out of a JSON document

$ echo '{"services":{"web":{"ports":[8080,8443]}}}' | treesift -c 'services.web.ports[0]'
8080

Flattening across every key of an object

The * step walks every service, and [] flattens each of their port lists into one stream.

$ echo '{"services":{"web":{"ports":[8080,8443]},"db":{"ports":[5432]}}}' \
    | treesift -c 'services.*.ports[]'
8080
8443
5432

YAML, and dropping the JSON quotes

-r prints strings bare, which is what you want when the result is going into a shell loop rather than onto a screen.

$ cat users.yaml
users:
  - name: alice
    roles: [admin, dev]
  - name: bob
    roles: [dev]

$ treesift -rc 'users[].name' users.yaml
alice
bob

A list of objects without spelling out the brackets

A key step applied to a list is distributed over its elements, so this is the same selector as above with less punctuation.

$ treesift -rc 'users.name' users.yaml
alice
bob

XML attributes

Attributes become ordinary keys, and repeated elements become a list, so the same selector shape works here too.

$ echo '<config><server host="a.example"/><server host="b.example"/></config>' \
    | treesift -rc 'config.server[].host'
a.example
b.example

Branching on absence in a script

With -e the exit status carries the answer, so a script does not have to inspect the output to find out whether anything matched.

$ echo '{}' | treesift -e 'database.host'
$ echo $?
1
if ! host=$(treesift -re 'database.host' config.yaml); then
    echo "no database.host in config.yaml" >&2
    exit 1
fi

Errors are quiet and consistent

A malformed document is reported on stderr and exits 2, rather than producing a stack trace that a calling script has to parse.

$ echo '{"a":' | treesift -c 'a'
treesift: could not parse JSON: Expecting value: line 2 column 1 (char 6)
$ echo $?
2

A selector that simply matches nothing is not an error — it prints nothing and exits 0, unless -e asked otherwise.