3

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.

3
  • Does this answer your question? Make the Python json encoder support Python's new dataclasses Commented Jan 11, 2022 at 3:50
  • @Bharel: Yes, it does. Thank you. Commented Jan 11, 2022 at 3:55
  • So you just need one-way translation, right - from dataclass to JSON? Commented Jan 12, 2022 at 15:47

2 Answers 2

6

You wish the encoder to support both Decimal and dataclasses. 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)
Sign up to request clarification or add additional context in comments.

Comments

0

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"]}]}

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.