概要
JSON をロードしたときなど、配列とdictが何重にもネストしたような複雑な構造のdictオブジェクトができます。
このオブジェクトをアクセスしやすくする(とくにエラーまわりを解りやすくする)下記の機能を提供します。
- (中間でも末端でも)キーがないときにフルパスで解りやすいエラー:
hoge = DataSelector(data)
hoge['aaa']['bbb']['ccc']
# ccc というキーは存在しないときのエラー
# path "ROOT.aaa.bbb.ccc" is not found!
# bbb というキーは存在しないときのエラー
# path "ROOT.aaa.bbb" is not found!
for v in hoge['aaa']['bbb']['ccc']:
# ccc が配列でないときのエラー
# path "ROOT.aaa.bbb.ccc" is not list!
for k, v in hoge['aaa']['bbb']['ccc'].items():
# ccc が dict でないときのエラー
# path "ROOT.aaa.bbb.ccc" is not dict!
- 値にアクセスしやすくするセレクタ:
hoge = DataSelector(data)
# 値の取得
hoge['aaa']
# 複数階層も可
hoge['aaa.bbb']
# 値がない場合にデフォルト値(通常ならエラーが出る) ※ aaa がない場合でもデフォルト値
hoge.find('aaa.ccc', '')
# dict でループ
for k, v in hoge.items():
for k in hoge.keys():
for v in hoge.values():
# リストとしてループ
for v in hoge:
定義するクラス
class DataSelector:
def __init__(self, data, parent_path='ROOT'):
self.parent_path=parent_path
self.data=data
def __getitem__(self, path):
return self.find(path)
def _check_dict(self):
if type(self.data) != dict:
raise Exception(f'path "{self.parent_path}" is not dict!')
def _check_list(self):
if type(self.data) != list:
raise Exception(f'path "{self.parent_path}" is not list!')
def __iter__(self):
self._check_list()
for i, v in enumerate(self.data):
yield DataSelector(v, f'{self.parent_path}[{i}]')
def items(self):
self._check_dict()
for k, v in self.data.items():
yield k, DataSelector(v, f'{self.parent_path}.{k}')
def keys(self):
self._check_dict()
return self.data.keys()
def values(self):
self._check_dict()
for k, v in self.data.items():
yield DataSelector(v, f'{self.parent_path}.{k}')
def find(self, path, default_val=None):
paths = path.split(".") if type(path) == str else [path]
_data = self.data
_path = self.parent_path
for p in paths:
_path = f'{_path}.{p}'
if p in _data:
_data = _data[p]
elif default_val is not None:
_data = default_val
else:
raise Exception(f'path "{_path}" is not found!')
if type(_data) == list or type(_data) == dict:
return DataSelector(_data, _path)
else:
return _data
使い方
res = list()
root = DataSelector(data)
for k1, v1 in root.items():
# もしも root が dict でないなら下記のエラーで教えてくれる
# path "ROOT" is not dict!
bbb = v1['aaa.bbb']
# もしも aaa や bbb が存在しないなら下記のエラーで教えてくれる
# path "ROOT.aaa" is not found!
# path "ROOT.aaa.bbb" is not found!
for v2 in bbb:
# もしも bbb がリストでないなら下記のエラーで教えてくれる
# path "ROOT.aaa.bbb" is not list!
res.append(dict(eee=v2['ccc']))
# もしも ccc が存在しないなら下記のエラーで教えてくれる
# path "ROOT.aaa.bbb[0].ccc" is not found!