Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Json serialization of nested dataclasses

I would need to take the question about json serialization of @dataclass from Make the Python json encoder support Python's new dataclasses a bit further: consider when they are in a nested structure.

Consider:

import json
from attr import dataclass
from dataclasses_json import dataclass_json
@dataclass
@dataclass_json
class Prod:
    id: int
    name: str
    price: float

prods = [Prod(1,'A',25.3),Prod(2,'B',79.95)]
pjson = json.dumps(prods)

That gives us:

TypeError: Object of type Prod is not JSON serializable

Note the above does incorporate one of the answers https://stackoverflow.com/a/59688140/1056563 . It claims to support the nested case via the dataclass_json decorator . Apparently that does not actually work.

I also tried another of the answers https://stackoverflow.com/a/51286749/1056563 :

class EnhancedJSONEncoder(json.JSONEncoder):
        def default(s, o):
            if dataclasses.is_dataclass(o):
                return dataclasses.asdict(o)
            return super().default(o)

And I created a helper method for it:

def jdump(s,foo):
    return json.dumps(foo, cls=s.c.EnhancedJSONEncoder)

But using that method also did not effect the (error) result. Any further tips?

like image 454
WestCoastProjects Avatar asked Apr 02 '20 20:04

WestCoastProjects


3 Answers

You can use a pydantic library. From the example in documentation

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float
    foo: str
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

# returns a dictionary:
print(m.dict())
"""
{
    'banana': 3.14,
    'foo': 'hello',
    'bar': {'whatever': 123},
}
"""
print(m.dict(include={'foo', 'bar'}))
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(m.dict(exclude={'foo', 'bar'}))
#> {'banana': 3.14}
like image 83
Nikita Vostrosablin Avatar answered Oct 12 '22 17:10

Nikita Vostrosablin


import json
from dataclasses import dataclass, asdict


@dataclass
class Prod:
    id: int
    name: str
    price: float


prods = [asdict(Prod(1, 'A', 25.3)), asdict(Prod(2, 'B', 79.95))]
pjson = json.dumps(prods)
print(pjson)
# [{"id": 1, "name": "A", "price": 25.3}, {"id": 2, "name": "B", "price": 79.95}]
like image 35
Evgeniy_Burdin Avatar answered Oct 12 '22 17:10

Evgeniy_Burdin


This is actually not a direct answer but more of a reasonable workaround for cases where mutability is not needed (or desirable). The typing based NamedTuple looks and feels quite similar and is probably the inspiration behind the dataclass. If serialization were needed it is likely presently the best alternative.

from typing import NamedTuple

class Prod(NamedTuple):
    id: str
    name: str
    price: str

I made that as a drop-in replacement for the dataclass based Prod class and it works.

like image 1
WestCoastProjects Avatar answered Oct 12 '22 15:10

WestCoastProjects