Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dlamblin/e7ac11c48a33397c82cd2cc614734a76 to your computer and use it in GitHub Desktop.

Select an option

Save dlamblin/e7ac11c48a33397c82cd2cc614734a76 to your computer and use it in GitHub Desktop.
Work with JSON in dot notation when using Python

Python repl can autocomplete object attributes/members/fields

But the default JSON parsing gives you a dict not an object

So you may have been frustrated when the Python repl and your IDEs should have autocompletion, but they simply don't autocomplete dictionary keys of a parsed json object while you're working with a novel or one off json object.

To see what I mean consider this repl session:

>>> import json
>>> params="""{"required_jobs":["bills"],"org_name":"Bank","allocation":{"targets":[]},
"end_user_id":"ffffffff-be11-c0de-beeb-b00lleafdead","deposit_forms":"n/a","end_user":{"chef":null},
"has_user_authentication_data":true,"cards":[{"card_name":"Credit"},
{"card_name":"Debit"}],"solution":"Bills","features":["bills"],"for_api_version":"2025-01-01"}"""
>>> p=json.loads(params)
>>> p['solution']
'Bills'
>>> p['org_name']
'Bank'
>>> p[⭲
KeyboardInterrupt
>>> p.⭲
KeyboardInterrupt  p.get(         p.pop(         p.update(
>>> py()       p.items()      p.popitem()    p.values()

See how you can't autocomplete valid keys in p? At the it won't give you any of the dictionary keys, and was ^C interrupted, but it does give you attributes and methods. The text over-wrote KeyboardInterrupt and >>> over the first two lines of suggestions and cleared the rest of the suggested items.

Address this by giving the json.loads method an object_hook

Ours is a quick and dirty generic dataclass maker.

If p were an object you could get autocompletion of the data IN your JSON object. There's a fun way you can make that happen:

>>> from dataclasses import make_dataclass
>>> def mk_dc_obj(d: dict) -> object:
...     _C = make_dataclass('_'.join(d.keys()), d.keys())
...     return _C(**d)
...
>>> import json
>>> obj_params = json.loads(params, object_hook=mk_dc_obj)
>>> p.org_name
'Bank'
>>> obj_params.⭲
KeyboardInterrupttion                    obj_params.for_api_version
>>> params.cards                         obj_params.has_user_authentication_data

Note again, it had shown all of the suggestions but overwrote the interrupt and the prompt there. It looks like (note double tabbing):

>>> obj_params.⭲
[ not unique ]⭲
obj_params.allocation                    obj_params.for_api_version
obj_params.cards                         obj_params.has_user_authentication_data
obj_params.deposit_forms                 obj_params.org_name
obj_params.end_user                      obj_params.required_jobs
obj_params.end_user_id                   obj_params.solution
obj_params.features

and because you do it WITH the help of json.loads(..., object_hook=...) it goes deep enough to auto-complete within objects, like:

>>> obj_params.a⭲
>>> obj_params.allocation.⭲
>>> obj_params.allocation.targets
[]

Note

This is probably not a good idea for a large project, as your class names are generated based on the keys and might conflict with in use classes (probably not, if you follow the casing styles). And in the repl it sort of loses track of the type inside a list, objects in lists don't autocomplete their attributes, fields, members, unless you assign them to a name and try completing on the name.

>>> obj_params.__class__.__name__
'required_jobs_org_name_allocation_end_user_id_deposit_forms_end_user_has_user_authentication_data_cards_solution_features_for_api_version'
>>> obj_params.allocation.__class__.__name__
'targets'
>>> obj_params.end_user.__class__.__name__
'platform_matching'
>>> obj_params.cards[0].__class__.__name__
'card_name'
>>>
>>> card0 = obj_params.cards[0].⭲  # Does not autocomplete anything here
>>> card0 = obj_params.cards[0]
>>> card0.⭲  # Does autocomplete as shown below
>>> card0.card_name
'Credit'
>>>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment