【Tips】jqが入ってなくてもjsonをpythonで見やすくする方法

2015.06.24

サーモン大好き、横山です。 jsonを見るときに、大活躍するjqですが、「入れたと思ったサーバに実は入ってなかった!」という経験があるんじゃないでしょうか? Amazon Linuxの場合ですと、

$ sudo yum install -y jq

のコマンドを叩けばインストールできますが、今回は敢えてjqがなかった場合の方法をご紹介いたします。

この記事はjqの使用を抑制するような記事ではございません

jsonを取得する

今回は OpenWeatherMap のAPIを使い、東京の天気の情報をjsonで取得します。

$ curl -s "http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp"
{"coord":{"lon":139.69,"lat":35.69},"sys":{"message":0.0179,"country":"JP","sunrise":1435087582,"sunset":1435140053},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"base":"stations","main":{"temp":296.46,"temp_min":296.46,"temp_max":296.46,"pressure":1020.61,"sea_level":1025.39,"grnd_level":1020.61,"humidity":82},"wind":{"speed":1.46,"deg":25.0075},"clouds":{"all":64},"dt":1435109040,"id":1850147,"name":"Tokyo","cod":200}

通常取得すると、このように1行表示になって見にくいですね。 これをPythonを使って見やすく表示します。

jsonモジュールを使って整形しよう

今回のPythonのバージョンは2.7.9を使用しています。

$ python -V
Python 2.7.9

ということで、スクリプトです。
方針として、標準入力でJSONを受け取り、JSONを一旦Pythonの辞書型に変換します。
その後辞書型を整形したJSON文字列へ変換します。

format_json.py

#!/usr/bin/env python
# encoding: utf-8
import json

weather_dict = json.loads(raw_input())
weather_format_json = json.dumps(weather_dict, indent=4, separators=(',', ': '))
print weather_format_json

使用例

$ chmod +x format_json.py
$ curl -s "http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp" | ./format_json.py
{
    "clouds": {
        "all": 64
    },
    "name": "Tokyo",
    "coord": {
        "lat": 35.69,
        "lon": 139.69
    },
    "sys": {
        "country": "JP",
        "message": 0.0179,
        "sunset": 1435140053,
        "sunrise": 1435087582
    },
    "weather": [
        {
            "main": "Clouds",
            "id": 803,
            "icon": "04d",
            "description": "broken clouds"
        }
    ],
    "cod": 200,
    "base": "stations",
    "dt": 1435109040,
    "main": {
        "temp": 296.46,
        "grnd_level": 1020.61,
        "temp_max": 296.46,
        "sea_level": 1025.39,
        "humidity": 82,
        "pressure": 1020.61,
        "temp_min": 296.46
    },
    "id": 1850147,
    "wind": {
        "speed": 1.46,
        "deg": 25.0075
    }
}

このように、見やすく表示することが出来ました。

ワンライナーでもできる

お気づきかと思いますが、実はこのスクリプト書かなくても、ワンライナーで済ませることができます。
(ただ、毎回コマンド叩く必要がある場合はスクリプトにしてしまったほうが楽です)

$ curl -s "http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp" | python -c "import json as j;print j.dumps(j.loads(raw_input()),indent=4,separators=(',',': '))"

上記のコマンドを叩いて貰えれば先ほどと同じ結果が得られます。

※ 追記 (2015/06/24 17:00)

以下の反応頂きました。

Pythonワンライナーのやつは、スクリプト書かない手が Stackoverflow先生のアンサーにありましたよー。 http://stackoverflow.com/a/1920585

情報元全文ツイートはこちら

こちらを知りませんでした。上記のJSONの整形処理を、Python2.6+にて$ python -m json.toolで出来るそうです。
以下、Python2.7で試しました。

$ curl -s "http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp" | python -m json.tool
{
    "base": "stations",
    "clouds": {
        "all": 76
    },
    "cod": 200,
    "coord": {
        "lat": 35.69,
        "lon": 139.69
    },
    "dt": 1435130328,
    "id": 1850147,
    "main": {
        "grnd_level": 1018.23,
        "humidity": 71,
        "pressure": 1018.23,
        "sea_level": 1023.09,
        "temp": 298.636,
        "temp_max": 298.636,
        "temp_min": 298.636
    },
    "name": "Tokyo",
    "rain": {
        "3h": 0.49
    },
    "sys": {
        "country": "JP",
        "message": 0.0124,
        "sunrise": 1435087582,
        "sunset": 1435140053
    },
    "weather": [
        {
            "description": "light rain",
            "icon": "10d",
            "id": 500,
            "main": "Rain"
        }
    ],
    "wind": {
        "deg": 166.004,
        "speed": 3.11
    }
}

表示することが出来ました。
ワンライナーですとこちらのほうが圧倒的に楽できますね。
ご指摘ありがとうございました。

せっかくですので、/usr/lib/python2.7/json/tool.pyの中身を見てみます。

r"""Command-line tool to validate and pretty-print JSON

Usage::

    $ echo '{"json":"obj"}' | python -m json.tool
    {
        "json": "obj"
    }
    $ echo '{ 1.2:3.4}' | python -m json.tool
    Expecting property name enclosed in double quotes: line 1 column 3 (char 2)

"""
import sys
import json

def main():
    if len(sys.argv) == 1:
        infile = sys.stdin
        outfile = sys.stdout
    elif len(sys.argv) == 2:
        infile = open(sys.argv[1], 'rb')
        outfile = sys.stdout
    elif len(sys.argv) == 3:
        infile = open(sys.argv[1], 'rb')
        outfile = open(sys.argv[2], 'wb')
    else:
        raise SystemExit(sys.argv[0] + " [infile [outfile]]")
    with infile:
        try:
            obj = json.load(infile)
        except ValueError, e:
            raise SystemExit(e)
    with outfile:
        json.dump(obj, outfile, sort_keys=True,
                  indent=4, separators=(',', ': '))
        outfile.write('\n')


if __name__ == '__main__':
    main()

主に、「標準入力->標準出力」「ファイル入力->標準出力」「ファイル入力->ファイル出力」対応しています。
以下のように、使用することが出来ました

$ python -m json.tool                        # 標準入力のJSONを、標準出力で整形して表示
$ python -m json.tool input.json             # input.jsonのJSONを、標準出力で整形して表示
$ python -m json.tool input.json output.json # input.jsonのJSONを、output.jsonに整形して表示

まとめ

実際にこの方法は、python内のjsonを綺麗に標準出力したい、ログに出力したい場合に使えるテクニックだと思います。
「jsonが見にくいけど、jqが使えない…」という場面での一つの手段として覚えておいても損はないと思います。