简体   繁体   中英

Python: converting class objects to JSON - object is not JSON serializable

I have a list of objects in my Python code. Each object is of type Outer with associated Inner objects - the classes are defined like this:

from decimal import Decimal

@dataclass
class Inner:
    col_a: Decimal
    col_b: str
    col_c: List['str']

@dataclass
class Outer:
    col_a: str
    col_b: Decimal
    col_c: List[Inner]

I would like to convert these objects into JSON. As I am using Decimal, I was hoping to just create my own encoder and use it in conjunction with json.dumps():

class DecimalJsonEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Decimal):
            return str(obj)
        else:
            return super(DecimalJsonEncoder, self).default(obj)

... and...

my_json = json.dumps(my_list, cls=DecimalJsonEncoder)

However, when I create a list of Outer objects (my_list) and try to create JSON, I get this error:

TypeError: Object of type Outer is not JSON serializable

I'm using Python 3.7.

Thanks in advance for any assistance.

You wish the encoder to support both Decimal and dataclass es. You can do it like so:

import dataclasses, json

class JSONEncoder(json.JSONEncoder):
        def default(self, o):
            if dataclasses.is_dataclass(o):
                return dataclasses.asdict(o)
            if isinstance(obj, Decimal):
                return str(obj)
            return super().default(o)

json.dumps(foo, cls=JSONEncoder)

Just adding an alternate solution that can work for you. This can be especially useful if you need to de-serialize (load) JSON data back to the nested dataclass model.

This uses an external library dataclass-wizard , which is a JSON serialization framework built on top of dataclasses. The example below should work for Python 3.7+ with the included __future__ import.

from __future__ import annotations

from dataclasses import dataclass
from decimal import Decimal

from dataclass_wizard import JSONWizard


@dataclass
class Outer(JSONWizard, str=False):
    col_a: str
    col_b: Decimal
    col_c: list[Inner]


@dataclass
class Inner:
    col_a: Decimal
    col_b: str
    col_c: list[str]


def main():
    obj = Outer(col_a='abc',
                col_b=Decimal('1.23'),
                col_c=[Inner(col_a=Decimal('3.21'),
                             col_b='xyz',
                             col_c=['blah', '1111'])])
    
    print(obj)
    
    print(obj.to_json())
    # {"colA": "abc", "colB": "1.23", "colC": [{"colA": "3.21", "colB": "xyz", "colC": ["blah", 1111]}]}
    
    # assert we get the same object when de-serializing the string data
    assert obj == obj.from_dict(obj.to_dict())


if __name__ == '__main__':
    main()

Output:

Outer(col_a='abc', col_b=Decimal('1.23'), col_c=[Inner(col_a=Decimal('3.21'), col_b='xyz', col_c=['blah', '1111'])])
{"colA": "abc", "colB": "1.23", "colC": [{"colA": "3.21", "colB": "xyz", "colC": ["blah", "1111"]}]}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM