jsonを直接引数としてコマンドラインから渡す方法を考えてた

はじめに

awscliはbotocoreにコマンドラインインターフェイスを持たせるためのラッパーでしかないと考えると色々捗る気がした。 つまり基本的にはJSONを入力として取るAPIが存在してそれにより何でも操作する。 ただし、直接APIを呼ぶコードを書くのは面倒くさいので、シェル上から簡易に使えるコマンドが存在するというイメージ。

しかしJSONを引数に取れるようにと考えてみると以下の2つがあってつらい

  • jsonはネスト可能な構造
  • jsonにはArrayが存在

jsonの作成

なんとなくワンライナー的な形で作成できるような形にしたかったのでシェル・コマンド的な文法を持つミニ言語を考えてみた。 以下のようなコードが

mkobject person
cd person
put name foo; put age 20

以下のようなjsonを返す。

{
  "person": {
    "name": "foo", 
    "age": "20"
  }
}

ここで気になるのは、フィールドの順序。もちろんpythonのdictを使った場合にはフィールドの順序は保証されない。 ただ、json出力に限っては記述した順序が保証されても良いのではないかなと思いOrderedDictを使っている。

おおよそシェルのファイル構造に似たような物を利用してjsonを作っていく。相対パス絶対パスにも対応してみたので以下の様なコードも動く

mkobject address phoneNumber
cd address
put ./streetAddress "21 2nd Street"
put city "New York"
put ../phoneNumber/location home
put /phoneNumber/code 44

これで出力されるJSONは以下。

{
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York"
  },
  "phoneNumber": {
    "code": "44",
    "location": "home"
  }
}

arrayへの対応

困ったのはArrayでappendというコマンドを別途作ったのだけれど。putでもkeyがdigitとみなせるものだったらdigitとして扱うことにした。 ただし新しい要素の追加に使えないのでSelfishListというクラスを別途作った。

class SelfishList(list):
    def __setitem__(self, i, v):
        try:
            super(SelfishList, self).__setitem__(i, v)
        except IndexError:
            if len(self) == i:
                self.append(v)
            else:
                raise


L = SelfishList()
L[0] = 10 # [10]
L[100000] # raise IndexError

このおかげで以下の様に書けるようになった。

mkarray favorites
append favorites a
append favorites b

もしくは

mkarray favorites
cd favorites
put 0 a
put 1 b

結果は

{
  "favorites": [
    "a",
    "b"
  ]
}

cpの追加

cpもあると面白いかもしれないと思いcpも作った。以下の様に動く。

mkobject foo
cd foo
put name "foo"
put age 20
cd ../
mkarray members
cp foo members/0
cp foo members/1
put members/1/name "bar"
rm foo

これは一時的にfooというobjectを作りそれを改変してmembersの要素を作っている。fooは一時的なファイルのようなものだったので不要になったら取り除いている。

{
  "members": [
    {
      "name": "foo", 
      "age": "20"
    }, 
    {
      "name": "bar", 
      "age": "20"
    }
  ]
}

これは1行で以下の様に書いても良い。

mkobject foo;cd foo;put name "foo";put age 20;cd ../;mkarray members;cp foo members/0;cp foo members/1;put members/1/name "bar";rm foo

gist

jsonmaker.py · GitHub