JSONはとても便利なデータ形式ですが、構造の自由度が大きいのでその全体像を掴みづらい時があります。特に、辞書とリストが何層にも入り混じって複雑な木構造になっているやつは、目的のデータにたどり着くために何回もキーを指定する必要があります。全体のツリー構造とキーと代表値がわかれば便利なので、そのような関数を書きました。既にそのようなモジュールがあったらすいません。
とりあえずコードだけ先に
def json_tree(data,indent=0):
space = ' '*indent
if type(data) == dict:
for k in data.keys():
print('\n',space,k,end='')
json_tree(data[k],indent+4)
elif type(data) == list and len(data) > 0:
json_tree(data[0],indent+4)
else:
print(' :',data,end='')
return
JSONとは
Python流の解釈をすれば、「辞書またはリストまたはその他の値を含む「辞書またはリストまたはその他の値(以下無限ループ)」」です。つまり、**「辞書またはリストまたはその他の値」をJSONと定義すれば、JSONとは再帰的に「JSONを含むJSON」**と定義することができます。
まあ、そんな言葉遊びみたいなことは置いておいて、
- 辞書型でキー・バリューのペアを設定してもよい
- 辞書型をリストにしてデータベースみたいにしてもいい
というルールで、階層的なデータをキーで指定しやすくしたものと考えることができます。
つまり、図示すると以下のようになります。
つまり、経路はどうあれ、最終的には必ず末端の「その他の値」に行き着きます。リスト型は通常、データベースのように同じキーを持つ辞書型が列挙されていることが想定されているので、その一行目を共通の辞書型として扱うことができます。
よって、
- 辞書型を経由した時点で、インデントを下げて、全キーに対し、キーを表示しそのバリューを解析
- リスト型を経由した時点で、その一行目を解析
- その他の値を経由した時点で、値を表示
という手順を繰り返せば、いい感じにキー・バリューペアのツリー構造を表示できそうです。
再帰関数として実装
下の図は、再帰関数を組むにあたって、上の流れをもう少し具体的に図示したものです。
一種の例外処理として、リストの長さが 0 だった時には、それをあたかも「空のリストという値」であるかのふるまうようにします。こうしないと、空のリストがあった時に、一行空けて無が出力されてしまうので。また、改行が行われる時は「新たなキーが検出された時」だけなので、それに気をつければ以下のような実装になります。
def json_tree(data,indent=0):
space = ' '*indent
if type(data) == dict:
for k in data.keys():
print('\n',space,k,end='')
json_tree(data[k],indent+4)
elif type(data) == list and len(data) > 0:
json_tree(data[0],indent+4)
else:
print(' :',data,end='')
return
ランダムに生成されたJSONに対して試してみると、以下のような結果が得られます。
_id : 60ef08e86b3a1582f0d06063
index : 0
guid : 076489f7-98a3-489e-807b-5e43fbd816a8
isActive : True
balance : $2,974.36
picture : http://placehold.it/32x32
age : 22
eyeColor : blue
name : Concetta Stewart
gender : female
company : BEZAL
email : concettastewart@bezal.com
phone : +1 (959) 448-2023
address : 656 Wortman Avenue, Interlochen, South Dakota, 9988
about : Officia ipsum veniam ex aute. # 長いので省略
registered : 2017-09-24T11:13:26 -09:00
latitude : 2.283491
longitude : 35.920546
tags : dolore
friends
id : 0
name : Johnston Rosales
greeting : Hello, Concetta Stewart! You have 8 unread messages.
favoriteFruit : strawberry
辞書型の中にリストを含むような場合(上のfriends
)でもちゃんと木構造を検出できていることがわかります。
ついでに、あるゲームで使われているjsonデータを対象にした例。
_version : 2.2.0
_customData
_time : 74
_BPMChanges : []
_bookmarks
_time : 8
_name : 1A
_events : []
_notes
_time : 8.0625
_lineIndex : 1
_lineLayer : 0
_type : 0
_cutDirection : 1
_obstacles
_time : 8
_lineIndex : 3
_type : 0
_duration : 4
_width : 1
_waypoints : []
空のリストがあった時に、ちゃんとバリューとして[]
が返されていることがわかります。