5

Generally, we can define __str__ method to make str(obj) return what we want.

But now I want to define my Model object to return a default JSON string when using json.dumps(obj).

Is there any nice way for me to declare a method in the class to do this?

class MyClass:
    ...
    def __json__(self):
        return {'name': self.name, 'age': self.age}

obj = MyClass()

json.dumps(obj) # returns the same as json.dumps(obj.__json__)
otus
  • 5,572
  • 1
  • 34
  • 48
Alfred Huang
  • 17,654
  • 32
  • 118
  • 189

2 Answers2

9

If you only need Python -> JSON, it's simple to write a JSONEncoder class that calls such a method for any object:

class AutoJSONEncoder(JSONEncoder):
    def default(self, obj):
        try:
            return obj._json()
        except AttributeError:
            return JSONEncoder.default(self, obj)

You can then use the class directly AutoJSONEncoder().encode(obj) or through the dumps interface json.dumps(obj, cls=AutoJSONEncoder).

The reverse direction requires at least a list of classes for which to call a _fromjson method.

(Note: __foo__ names are reserved so you shouldn't define them for your own purposes. __bar invokes name mangling, which probably isn't what you want.)

otus
  • 5,572
  • 1
  • 34
  • 48
  • Well, this is I want. But I tried the code, it seems not works by default, how can it be? – Alfred Huang Jun 04 '14 at 06:58
  • You need to make sure you are using the custom class instead of the default JSONEncoder. You can pass the class name to `json.dumps` using the `cls` parameter. If you already have a bunch of `dumps` calls and don't want to add it to all of them, you can define your own using `dumps = lambda obj: json.dumps(obj, cls=AutoJSONEncoder)`. – otus Jun 04 '14 at 07:02
  • 1
    OK, thanks alot! I edited your answer to add the call method. – Alfred Huang Jun 04 '14 at 07:12
  • For more, when I tried `AutoJSONEncoder().encode([obj1, obj2])`, I got an error, can this be possible? – Alfred Huang Jun 04 '14 at 08:10
  • It raises `[<...>] is not JSON serializable`, `Exception Type: TypeError`, I just want it automatically cascading, is that possible? @otus – Alfred Huang Jun 04 '14 at 08:29
  • @fish_ball, that kind of code (e.g. [this](https://gist.github.com/jvarho/f3967afb112aa4a6de55)) works for me. You should show more code and perhaps ask a new question if you can't figure it out. – otus Jun 04 '14 at 08:34
7
import json


class MyEncoder(json.JSONEncoder):
    """
    JSONEncoder subclass that leverages an object's `__json__()` method,
    if available, to obtain its default JSON representation. 

    """
    def default(self, obj):
        if hasattr(obj, '__json__'):
            return obj.__json__()
        return json.JSONEncoder.default(self, obj)


class MyClass(object):
    name = 'John'
    age = 30

    def __json__(self):
        return {'name': self.name, 'age': self.age}

>>> json.dumps(MyClass(), cls=MyEncoder)
{"age": 30, "name": "John"}
Jakub Roztocil
  • 15,930
  • 5
  • 50
  • 52