0

I'd like to extend the JSONEncoder to call __json__ of an object to convert it to something serializable.

I've tried this:

import json

class Foo(object):

    def __json__(self):
        return dict(a=1, b=2)

class MyJSONEncoder(json.JSONEncoder):
      """
      Use __json__ attr or callable of object for JSON serialization.
      """
      def default(self, o):
          if hasattr(o, '__json__'):
              if callable(o.__json__):
                  o = o.__json__()
              else:
                  o = o.__json__
          return super(MyJSONEncoder, self).default(o)

json.dumps(Foo(), cls=MyJSONEncoder)

And get this:

Traceback (most recent call last):
  File "./foo.py", line 20, in <module>
    json.dumps(Foo(), cls=MyJSONEncoder)
  File "/usr/lib64/python2.7/json/__init__.py", line 250, in dumps
    sort_keys=sort_keys, **kw).encode(obj)
  File "/usr/lib64/python2.7/json/encoder.py", line 207, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib64/python2.7/json/encoder.py", line 270, in iterencode
    return _iterencode(o, 0)
  File "./foo.py", line 18, in default
    return super(MyJSONEncoder, self).default(o)
  File "/usr/lib64/python2.7/json/encoder.py", line 184, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: {'a': 1, 'b': 2} is not JSON serializable

1 Answer 1

1

I found my error:

class MyJSONEncoder(DjangoJSONEncoder):                                         
      """                                                                         
      Use __json__ attr or callable of object for JSON serialization.             
      """                                                                         

      def default(self, o):                                                       
          if hasattr(o, '__json__'):                                              
              if callable(o.__json__):                                            
                  return o.__json__() # <----- here                                            
              else:                                                               
                  return o.__json__ # <-------- and here                                              
          return super(MyJSONEncoder, self).default(o)         

The initial implementation mistakenly passed the o.__json__ results up the chain to the parent's default(), with the assumption that default() should return a serialized object.. This isn't how it works. It needs to return a serializable object.

The source of json/encoder.py summarizes this well:

To extend this to recognize other objects, subclass and implement a .default() method with another method that returns a serializable object for o if possible, otherwise it should call the superclass implementation (to raise TypeError).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.