python - object_hook in json module doesn't seem to work as I'd expect -


i'm having trouble understanding how object_hook functionality json.loads() works. found similar question object_hook not address full json here, i've tried follow understand it, , it's still not working me. gathered object_hook function called recursively in way, i'm failing understand how use construct complex object hierarchies json string. consider following json string, classes, , object_hook function:

import json pprint import pprint  jstr = '{"person":{ "name": "john doe", "age": "46", \            "address": {"street": "4 yawkey way", "city": "boston", "state": "ma"} } }'  class address:     def __init__(self, street=none, city=none, state=none):         self.street = street         self.city = city         self.state = state  class person:     def __init__(self, name=none, age=none, address=none):         self.name = name         self.age = int(age)         self.address = address(**address)  def as_person(jdict):     if u'person' in jdict:         print('person found')         person = jdict[u'person']         return person(name=person[u'name'], age=person[u'age'],                        address=person[u'address'])     else:         return('person not found')         return jdict 

(i define classes keyword args provide defaults json need not contain elements, , can still ensure attributes present in class instance. associate methods classes, want populate instances json data.)

if run:

>>> p = as_person(json.loads(jstr)) 

i expect, ie:

person found 

and p becomes person object, ie:

>>> pprint(p.__dict__) {'address': <__main__.address instance @ 0x0615f3c8>,  'age': 46,  'name': u'john doe'} >>> pprint(p.address.__dict__) {'city': u'boston', 'state': u'ma', 'street': u'4 yawkey way'} 

however, if instead, try use:

>>> p = json.loads(jstr, object_hook=as_person) 

i get:

person found traceback (most recent call last):   file "<interactive input>", line 1, in <module>   file "c:\program files (x86)\python27\lib\json\__init__.py", line 339, in loads     return cls(encoding=encoding, **kw).decode(s)   file "c:\program files (x86)\python27\lib\json\decoder.py", line 366, in decode     obj, end = self.raw_decode(s, idx=_w(s, 0).end())   file "c:\program files (x86)\python27\lib\json\decoder.py", line 382, in  raw_decode     obj, end = self.scan_once(s, idx)   file "<interactive input>", line 5, in as_person typeerror: string indices must integers, not unicode 

i have no idea why happen, , suspect there subtlety around how object_hook mechanism works i'm missing.

in attempt incorporate notion aforementioned question, object_hook evaluates each nested dictionary bottom (and replaces in traverse?) tried:

def as_person2(jdict):     if u'person' in jdict:         print('person found')         person = jdict[u'person']         return person2(name=person[u'name'], age=person[u'age'], address=person[u'address'])     elif u'address' in jdict:         print('address found')         return address(jdict[u'address'])     else:         return('person not found')         return jdict  >>> json.loads(jstr, object_hook=as_person2) address found person found traceback (most recent call last):   file "<interactive input>", line 1, in <module>   file "c:\program files (x86)\python27\lib\json\__init__.py", line 339, in loads     return cls(encoding=encoding, **kw).decode(s)   file "c:\program files (x86)\python27\lib\json\decoder.py", line 366, in decode     obj, end = self.raw_decode(s, idx=_w(s, 0).end())   file "c:\program files (x86)\python27\lib\json\decoder.py", line 382, in raw_decode     obj, end = self.scan_once(s, idx)   file "<interactive input>", line 5, in as_person2 attributeerror: address instance has no attribute '__getitem__' 

so, clearly, proper form of object_hook function escaping me.

can explain in detail how object_hook mechanism works, , how resulting object tree supposed recursively constructed bottom up, why code doesn't work expected, , either fix example or provide 1 uses object_hook function build complex class, given 1 object_hook function?

through experimentation, have answered own question; may not best solution, , welcome further analysis or better way, sheds light on how object_hook process works, may instructive others facing same issues.

the key observation that, @ every level of json tree walk, object_hook mechanism expects return dictionary, if want change subdictionaries class instances, have replace current object_hook function invocation's input dictionary values objects, , not return object instances.

the solution below allows bottom-up means of building object hierarchy. i've inserted print statements show how loads object_hook called on on subsections of json string it's processed, found quite illuminating, , helpful me in build working function.

import json pprint import pprint  jstr = '{"person":{ "name": "john doe", "age": "46", \          "address": {"street": "4 yawkey way", "city": "boston", "state": "ma"} } }'  class address:     def __init__(self, street=none, city=none, state=none):         self.street=street         self.city=city         self.state = state     def __repr__(self):         return('address(street={self.street!r}, city={self.city!r},'                           'state={self.state!r})'.format(self=self))  class person:     def __init__(self, name=none, age=none, address=none):         self.name = name         self.age = int(age)         self.address=address     def __repr__(self):         return('person(name={self.name!r}, age={self.age!r},\n'                '       address={self.address!r})'.format(self=self))  def as_person4(jdict):     if 'person' in jdict:         print('person in jdict; (before substitution):')         pprint(jdict)         jdict['person'] = person(**jdict['person'])         print('after substitution:')         pprint(jdict)         print         return jdict     elif 'address' in jdict:         print('address in jdict; (before substitution):'),         pprint(jdict)         jdict['address'] = address(**jdict['address'])         print('after substitution:')         pprint(jdict)         print         return jdict     else:         print('jdict:')         pprint(jdict)         print         return jdict  >>> p =json.loads(jstr, object_hook=as_person4) jdict: {u'city': u'boston', u'state': u'ma', u'street': u'4 yawkey way'}  address in jdict; (before substitution): {u'address': {u'city': u'boston', u'state': u'ma', u'street': u'4 yawkey way'}, u'age': u'46', u'name': u'john doe'} after substitution: {u'address': address(street=u'4 yawkey way', city=u'boston', state=u'ma'), u'age': u'46', u'name': u'john doe'}  person in jdict; (before substitution): {u'person': {u'address': address(street=u'4 yawkey way', city=u'boston', state=u'ma'),          u'age': u'46', u'name': u'john doe'}} after substitution: {u'person': person(name=u'john doe', age=46,    address=address(street=u'4 yawkey way', city=u'boston', state=u'ma'))}  >>> p {u'person': person(name=u'john doe', age=46,    address=address(street=u'4 yawkey way', city=u'boston', state=u'ma'))} >>>  

note returned still dictionary, key 'person', , value person object (rather person object), solution provide extensible bottom-up object construction method.


Comments

Popular posts from this blog

Command prompt result in label. Python 2.7 -

javascript - How do I use URL parameters to change link href on page? -

amazon web services - AWS Route53 Trying To Get Site To Resolve To www -