I'm using the following code to validate a dictionary (a) against another dictionary (check_against). Unfortunately my code isn't very readable so I was wondering if there is a faster/cleaner built in solution to achieve the same results. Maybe I just haven't googled the right keywords but I haven't found any discussion on what I would consider to be a fairly common task.
check_against = {
'a' : str,
'b' : {
'c': int,
'd': int,
}
}
a = {
'a' : 1,
'c' : 1
}
def get_type_at_path(obj, chain):
_key = chain.pop(0)
if _key in obj:
return key_exists(obj[_key], chain) if chain else type(obj[_key])
def root_to_leaf_paths(tree, cur=()):
if isinstance(tree,dict):
for n, s in tree.items():
for path in root_to_leaf_paths(s, cur+(n,)):
yield path
else:
yield [cur,tree]
for path,value_type in root_to_leaf_paths(check_against):
a_value_type = get_type_at_path(a,list(path))
if a_value_type == None:
print(f"Missing key at path \"{list(path)}\"")
elif not a_value_type == value_type:
print(f"Value at path \"{list(path)}\" should be of type \"{value_type}\" but got {a_value_type}")
outputs
Value at path "['a']" should be of type "<class 'str'>" but got <class 'int'>
Missing key at path "['b', 'c']"
Missing key at path "['b', 'd']"
You can adjust your root_to_leaf_paths() function a bit to treat it as a general dict flattener. Flatten both the schema and the data. Then the comparison is trivial.
schema = {
'a' : str,
'b' : {
'c': int,
'd': int,
}
}
data = {
'a' : 1,
'c' : 1
}
def flatten(obj, path = tuple()):
if isinstance(obj, dict):
for k, v in obj.items():
yield from flatten(v, path + (k,))
else:
yield (path, obj)
fschema = dict(flatten(schema))
fdata = dict(flatten(data))
for path, exp in fschema.items():
if path in fdata:
got = type(fdata[path])
if got is not exp:
print(f'Incorrect type: path={path} got={got} exp={exp}')
else:
print(f'Missing key: path={path}')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With