Skip to content

Latest commit

 

History

History
executable file
·
372 lines (325 loc) · 8.4 KB

json-shell.md

File metadata and controls

executable file
·
372 lines (325 loc) · 8.4 KB

json-shell

Purpose

An interactive shell-like tool to explore a JSON file.

I am often frustrated by large complicated JSON objects and had this wild idea of treating an object like a Unix filesystem: there is a root, child nodes, and grandchildren, you can navigate with cd along with other familiar Unix commands.

Syntax

Syntax: json-shell [-v] json-filename

Options

Option Description Default
-v Enable verbose debugging Debugging is not enabled

Examples

Unlike a lot of my other tools, the examples are in the forms of interactive subcommands in the tool so I'll give examples of those.

Since there are so many subcommands and some of the help information is a little long, I'll provide a table of contents:

I'll also use the following JSON file in examples:

{
  "bools": [true, false],
  "ints": [42, 32768, -1],
  "floats": [3.141592654, 2.7182818285, -0.000001],
  "strs": ["a", "b", "foo", "bar"],
  "none": null,
  "dict": {
    "1": [".", "one", "One", "ONE"],
    "2": ["..", "two", "Two", "TWO"]
  }
}

This is also available from a Gist.

The prompt

The tool prompts for subcommands includes the current location much like the current working directory as in a filesystem.

$ json-shell sample.json
Confoozed?  Try `help`
/> 

The location is / since you always begin in the root node. The > is asking for a subcommand to be entered.

cat

cat will display the current or target node and all of its children.

/> cat
{
  "bools": [
    true,
    false
  ],
  "ints": [
    42,
    32768,
    -1
  ],
  "floats": [
    3.141592654,
    2.7182818285,
    -1e-06
  ],
  "strs": [
    "a",
    "b",
    "foo",
    "bar"
  ],
  "none": null,
  "dict": {
    "1": [
      ".",
      "one",
      "One",
      "ONE"
    ],
    "2": [
      "..",
      "two",
      "Two",
      "TWO"
    ]
  }
}
/> cd dict
/dict> cat
{
  "1": [
    ".",
    "one",
    "One",
    "ONE"
  ],
  "2": [
    "..",
    "two",
    "Two",
    "TWO"
  ]
}
/dict> cat 1
[
  ".",
  "one",
  "One",
  "ONE"
]
/dict>

cd

I'm taking great liberties with the name because we're not talking about a current working directory but it's a similar concept. You can cd into any child of the current node that's a list or dictionary:

/> cd bools
/bools> cd ..
/> cd dict
/dict> cd 1
/dict/1> cd
/> 

The keys you can cd into are based on the type of node you're currently in:

  • If the current node is a dictionary, the keys of the dictionary are the keys you can cd into.
  • If the current node is a list, the keys are integers from 0 to the length of the list minus one.

You don't have to treat the key differently based on whether you're in a list or dicitonary. The tool knows what type of key is needed.

There are invalid cds based on the current node:

/> cd foo
'foo' is not a key
/> cd floats
/floats> cd 3
3 is out of range
/floats> cd -1
-1 is out of range
/floats> cd 0
0 is not a list or dictionary
/floats> cd foo
'foo' is not an integer: invalid literal for int() with base 10: 'foo'
/floats> 

describe

The describe subcommand describes the current or target node:

/> describe
/ is a dict with 6 elements
/> describe ints
/ints is a list with 3 elements
/> cd ints
/ints> describe
/ints is a list with 3 elements
/ints> describe 0
/ints/0 is a int
/ints> 

exit

The exit and quit subcommands terminate the tool and return you to the Unix shell. The subcommands are synonymous.

find

The find command list elements starting at the current node, one node per line, with no indentation:

/> find
/
/bools/
/bools/0: True
/bools/1: False
/ints/
/ints/0: 42
/ints/1: 32768
/ints/2: -1
/floats/
/floats/0: 3.141592654
/floats/1: 2.7182818285
/floats/2: -1e-06
/strs/
/strs/0: 'a'
/strs/1: 'b'
/strs/2: 'foo'
/strs/3: 'bar'
/none: None
/dict/
/dict/1/
/dict/1/0: '.'
/dict/1/1: 'one'
/dict/1/2: 'One'
/dict/1/3: 'ONE'
/dict/2/
/dict/2/0: '..'
/dict/2/1: 'two'
/dict/2/2: 'Two'
/dict/2/3: 'TWO'
/> 

I find this kind of display very useful when I'm writing code to navigate through a structure to find specific elements.

grep

The grep command list elements starting at the current node, one node per line, that either match or do not match the regular expression(s):

Simple search

/> grep e
/bools/0: True
/bools/1: False
/floats/2: -1e-06
/none: None
/dict/1/1: 'one'
/dict/1/2: 'One'
/> 

Key-only search

/> grep e=
/none: None
/> 

Value-only search

/> grep =o
/strs/2: 'foo'
/none: None
/dict/1/1: 'one'
/dict/2/1: 'two'
/dict/2/2: 'Two'
/> 

Case-insensitive search

/> grep -i =o
/strs/2: 'foo'
/none: None
/dict/1/1: 'one'
/dict/1/2: 'One'
/dict/1/3: 'ONE'
/dict/2/1: 'two'
/dict/2/2: 'Two'
/dict/2/3: 'TWO'
/> 

Negated search

/> grep -vi =o
/
/bools/
/bools/0: True
/bools/1: False
/ints/
/ints/0: 42
/ints/1: 32768
/ints/2: -1
/floats/
/floats/0: 3.141592654
/floats/1: 2.7182818285
/floats/2: -1e-06
/strs/
/strs/0: 'a'
/strs/1: 'b'
/strs/3: 'bar'
/dict/
/dict/1/
/dict/1/0: '.'
/dict/2/
/dict/2/0: '..'
/> 

help

You can get general help of all subcommands:

/> help
Navigate around a JSON document, just like a shell, only different!

Commands:

  cat       Display the current node or a child
              `cat` by itself displays the current node
              `cat key` display child element `key` of the current node

  cd        Change the current node
              `cd` by itself goes to root node
              `cd ..` go to parent node as long as you're not already at the root
              `cd key` goes to a child node if the key exists and its node is a dictionary or list

  describe  Describe a node
              `describe` describes the current node
              `describe key` describes the child element `key` of the current node

  exit      Exit from json-shell

  find      List structure with each element on a separate line without indentation:
               key/key/.../key/key: value

  grep      Search structure for regular expressions
               grep re        # search for the regular expression in key or value
               grep re1=re2   # search for re1 in key and re2 in value
            
            Options:
               -i     perform case-insensitive search
               -v     show elements that do not match regular expression(s)
            
            Notes:
               Non-string values are cast to strings and then searched

  help      Display help

  ls        List keys in the current node

  pwd       Print the current node - note that this is included in each prompt

  quit      Exit from json-shell
/>

or you can get help on a specific subcommand:

/> help cd
Change the current node
  `cd` by itself goes to root node
  `cd ..` go to parent node as long as you're not already at the root
  `cd key` goes to a child node if the key exists and its node is a dictionary or list
/> 

ls

The ls subcommand displays the keys of the current node:

/> ls
bools  ints  floats  strs  none  dict
/> cd dict
/dict> ls
1  2
/dict> cd 1
/dict/1> ls
0  1  2  3
/dict/1> 

pwd

The pwd subcommand prompts the location of the current node - in a filesystem, you might think of this as the current working directory. It's also part of each and every prompt but it's such a popular command and it was an easy thing to add so I added it.

Notes

  • The tool leans heavily on the Python cmd module and does the following:
    • Tab-completion for subcommand verbs and potential arguments (keys)
    • Command history: Use the up and down arrows to go through subcommands previously issued
    • Command editing: use the left and right arrows to move around a subcommand to make changes
  • I have ideas for improvements. I was starting to list them hear but I've created individual issues for them.