2018/04/30

Python Tips: JSON を整形して表示したい

今回は JSON 形式の文字列を Python で整形して表示する方法をご紹介します。

今回は次の 2 つのパターンを取り上げてみます。

  • シンタックスハイライトなし
  • シンタックスハイライトあり

早速見ていきましょう。

シンタックスハイライトなし


シンタックスハイライトが特に要らない場合は、標準ライブラリの json だけを使えば OK です。

JSON が Python コード内で文字列として取得できている場合

JSON 形式の文字列が Python コードの中で取得できている場合は、いったん loads() で読み込んだ後に dumps() で再度ダンプし直すとよいでしょう。その際に dumps() のオプション引数 indent を指定することでインデントの大きさを決めることができます。

import json

JSON_SAMPLE = '{"_meta": {"hash": {"sha256": "hash"}, "pipfile-spec": 6, "requires": {"python_version": "3.6"}, "sources": [{"name": "pypi", "url": "https://pypi.python.org/simple", "verify_ssl": true } ] } }'
 
data = json.loads(JSON_SAMPLE)  
print(json.dumps(data, indent=2))

このコードの出力結果は次のとおりになります。

{
  "_meta": {
    "hash": {
      "sha256": "hash"
    },
    "pipfile-spec": 6,
    "requires": {
      "python_version": "3.6"
    },
    "sources": [
      {
        "name": "pypi",
        "url": "https://pypi.python.org/simple",
        "verify_ssl": true
      }
    ]
  }
}

JSON がファイルに格納されている場合

JSON 文字列がファイルに格納されている場合は、上の loads() の部分を load() に変えて、さらに、引数を文字列からファイルオブジェクトに変更すれば OK です。

また、 Python プログラムの中ではなくターミナル上で処理できればそれで十分な場合は、 json.tool モジュールを使う方法がお手軽でおすすめです。 json.tool は次の形で利用することができます。

$ python -m json.tool < data.json
{
  "_meta": {
    "hash": {
      "sha256": "hash"
    },
    "pipfile-spec": 6,
    "requires": {
      "python_version": "3.6"
    },
    "sources": [
      {
        "name": "pypi",
        "url": "https://pypi.python.org/simple",
        "verify_ssl": true
      }
    ]
  }
}

python -m json.tool に JSON 文字列を渡す方法には、上の「標準入力で渡す方法」の他に「ファイル名を引数として渡す方法」もあります。

$ python -m json.tool data.json

出力結果はどちらも同じです。

ちなみに、 python -m json.tool のヘルプは次のとおりになっています。

python -m json.tool -h
usage: python -m json.tool [-h] [--sort-keys] [infile] [outfile]

A simple command line interface for json module to validate and pretty-print JSON objects.

positional arguments:
  infile       a JSON file to be validated or pretty-printed
  outfile      write the output of infile to outfile

optional arguments:
  -h, --help   show this help message and exit
  --sort-keys  sort the output of dictionaries alphabetically by key

私は使ったことはありませんが、 dict 型のキーをアルファベット順(辞書順)に並べられる --sort-keys というオプションがあるようです。

標準ライブラリの json を使った方法については以上です。続いて シンタックスハイライトありの方法を見ていきましょう。

シンタックスハイライトあり

シンタックスハイライトを施して JSON を表示する機能は(私が知るかぎり) Python の標準ライブラリには無いので、何らかの非標準のライブラリを使用するか自分で書くかのどちらかになります。おすすめなのは pygments というライブラリを使用する方法です。

シンタックスハイライトなしの場合と同様に JSON が Python コード内で文字列として取得できている場合から見ていきましょう。

JSON が Python コード内で文字列として取得できている場合
pygments.highlight() 関数にコードの文字列を渡すと、シンタックスハイライトを施した形で文字列を返してくれるので、それを利用します。

import json

from pygments import highlight
from pygments.lexers import JsonLexer
from pygments.formatters import TerminalFormatter

JSON_SAMPLE = '{"_meta": {"hash": {"sha256": "hash"}, "pipfile-spec": 6, "requires": {"python_version": "3.6"}, "sources": [{"name": "pypi", "url": "https://pypi.python.org/simple", "verify_ssl": true } ] } }'

data = json.loads(JSON_SAMPLE)
formatted_data = json.dumps(data, indent=2)
print(highlight(formatted_data, JsonLexer(), TerminalFormatter()))

このコードの出力は次のとおりとなります。ターミナル上ではきれいにハイライトが行われて表示されます。

{
  "_meta": {
    "hash": {
      "sha256": "hash"
    },
    "pipfile-spec": 6,
    "requires": {
      "python_version": "3.6"
    },
    "sources": [
      {
        "name": "pypi",
        "url": "https://pypi.python.org/simple",
        "verify_ssl": true
      }
    ]
  }
}

出力をブラウザで利用したい場合等は、 TerminalFormatter の代わりに HtmlFormatter を利用することが利用できます。

import json

from pygments import highlight
from pygments.lexers import JsonLexer
from pygments.formatters import TerminalFormatter

JSON_SAMPLE = '{"_meta": {"hash": {"sha256": "hash"}, "pipfile-spec": 6, "requires": {"python_version": "3.6"}, "sources": [{"name": "pypi", "url": "https://pypi.python.org/simple", "verify_ssl": true } ] } }'

data = json.loads(JSON_SAMPLE)
formatted_data = json.dumps(data, indent=2)
print(highlight(formatted_data, JsonLexer(), TerminalFormatter()))

JSON がファイルに格納されている場合
JSON がファイルに格納されている場合は、もしターミナル上で扱えればよいだけであれば、 pygments が提供するコマンドラインツール pygmentize を使う方法がシンプルでおすすめです。

改行やインデントを保ったままハイライトだけができればよいのであれば、 pygmentize コマンドをそのまま使用すれば OK です。

$ pygmentize data.json
{"_meta": {"hash": {"sha256": "hash"}, "pipfile-spec": 6, "requires": {"python_version": "3.6"}, "sources": [{"name": "pypi", "url": "https://pypi.python.org/simple", "verify_ssl": true } ] } }

ファイルの拡張子が json の場合は自動で JSON と認識してくれるようです。拡張子が json 以外の場合は -l (--lexer) オプションでフォーマットが JSON であることを伝えれば OK です。

$ pygmentize -l json Pipfile.lock

改行やインデントをよきように調整してなおかつハイライトしてほしい場合は、標準ライブラリ json と組み合わせて使うとよいかと思います。 pygmentize コマンドも、対象のファイルが指定されなければ代わりに標準入力の文字列を処理してくれます。

$ python -m json.tool json_data.json | pygmentize -l json 
{
    "_meta": {
        "hash": {
            "sha256": "hash"
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "3.6"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.python.org/simple",
                "verify_ssl": true
            }
        ]
    }
}

ヘルプドキュメントの分量が多いのでここには掲載しませんが、 pygmentize には豊富なオプションがあるので、使ってみたい方は一度確認してから使ってみることをおすすめします。

$ pygmentize --help

以上です。

参考

2018/04/02

Python Tips:組み込みの名前を上書きしてしまったのを元に戻したい

Python で組み込み関数等の名前を上書きしてしまったときに元に戻す方法をご紹介します。

Python には、モジュールを import しなくても利用できる組み込みの関数があります。
例えば、 Python 3.6 の場合だと、その数は 60 以上にもなります。

abs() all() any() ascii() bin() bool() bytearray() bytes() callable() chr() classmethod() compile() complex() delattr()
dict() dir() divmod() enumerate() eval() exec() filter() float() format() frozenset() getattr() globals() hasattr() hash()
help() hex() id() input() int() isinstance() issubclass() iter() len() list() locals() map() max() memoryview()
min() next() object() oct() open() ord() pow() print() property() range() repr() reversed() round() set()
setattr() slice() sorted() staticmethod() str() sum() super() tuple() type() vars() zip() __import__()


これらの関数の名前を誤って上書きしてしまうと、元々あった関数の機能を使うことができません。多くの場合は変数名を変えればそれで済むのですが、 REPL や Jupyter を使っていると、 Python プロセスを起動し直す手間が大きく、稀に困ることがあります。

私の場合は、 idmapmax 等のシンプルな名前を変数名として使いたくなって、うっかり上書きしてしまうことがよくあります(他の言語のコードと Python コードを並行で触っているときによくやります)。

max = 10
max(10, 20, 5)
# => TypeError: 'int' object is not callable

結論としては、キーワード del を使って変数を削除すれば OK です。 del した後は元々の名前をまたそのまま利用できるようになります。

max = 10

del max

max(10, 20, 5)
# => 20

組み込みの名前は上書きしないように気をつけることが第一ですが、万が一上書きしてしまったときにはこういう方法があるということを覚えておくと便利です。

参考