I have a dictionary like this:
source = {
'Section 1' : {
'range' : [0, 200],
'template' : 'ID-LOA-XXX',
'nomenclature': True
},
'Section 2' : {
'range' : [201, 800],
'template' : 'ID-EPI-XXX',
'nomenclature': False,
'Subsection 1' : {
'range' : [0, 400],
'template' : 'ID-EPI-S1-XXX',
'nomenclature': False,
'Subsubsection 1' : {
'range' : [0, 400],
'template' : 'ID-EPI-S12-XXX',
'nomenclature': False
}
},
'Subsection 2' : {
'range' : [0, 400],
'template' : 'ID-EPI-S2-XXX',
'nomenclature': False
}
},
# etc.
}
which is loaded from a JSON file. I would like to 'flatten' that to the following dictionary:
target = {
'Section 1' : {
'range' : [0, 200],
'template' : 'ID-LOA-XXX',
'nomenclature': True,
'location' : './Section 1/'
},
'Section 2' : {
'range' : [201, 800],
'template' : 'ID-EPI-XXX',
'nomenclature': False,
'location' : './Section 2/'
},
'Subsection 1' : {
'range' : [0, 400],
'template' : 'ID-EPI-S1-XXX',
'nomenclature': False,
'location' : './Section 2/Subsection 1/'
},
'Subsubsection 1' : {
'range' : [0, 400],
'template' : 'ID-EPI-S12-XXX',
'nomenclature': False,
'location' : './Section 2/Subsection 1/Subsubsection 1'
},
'Subsection 2' : {
'range' : [0, 400],
'template' : 'ID-EPI-S2-XXX',
'nomenclature': False,
'location' : './Section 2/Subsection 2/'
},
# etc.
}
I may be able to change how the original JSON file is generated, but I'd prefer not to go there.
The JSON file in words: each section contains at least three keys, and may contain other keys. Those other keys are interpreted as subsections contained in the current section, and each one is a dict
with the same properties. This pattern may in principle recurse infinitely deep.
I would also like to perform some assertions:
- whether all required fields are present (
'range'
,'template'
and'nomenclature'
) - that the values of the required fields pass certain assertions
So far I've only managed to do these checks:
import json
key_requirements = {
"nomenclature": lambda x : isinstance(x, bool),
"template" : lambda x : isinstance(x, str) and "X" in x,
"range" : lambda x : isinstance(x, list) and len(x)==2 and all([isinstance(y,int) for y in x]) and x[1] > x[0]
}
def checkSection(section):
for key in section:
if key not in key_requirements:
checkSection(section[key])
elif not key_requirements[key]( section[key] ):
# error: assertion failed
pass
else:
# error: key not present
pass
for key in source # json.load(open(myJsonFile))
checkSection(data[key])
But at the moment, no amount of coffee has enabled me to come up with an efficient, elegant, pythonic way to weave the desired conversion into this scheme...
Any suggestions or ideas?