agstudy
agstudy

Reputation: 121608

construct an object from dict using cattr/attr with defaults set to None

I would like to recreate my object from a dictionary using cattr but it looks that this does not work with nested field set to None.

Here a code example that explains the problem:

import attr 
import cattr

@attr.s(auto_attribs=True) 
class B(object):
    b :int = 0

@attr.s(auto_attribs=True) 
class A(object):
    a :int = 0
    b: B = None

x = A(1) 
x_dict  = attr.asdict(x) 

Now if Iry to structure my object using:

cattr.structure(x_dict,A)

I get the following error:

   if 'b' in o:
TypeError: argument of type 'NoneType' is not iterable

One workaround is to remove None fields from the dict before call to structure :

del x_dict["b"]
cattr.structure(x_dict,A) 

Do I need to do this? or there is a simpler solution

Any help is much appreciated.

Upvotes: 2

Views: 1503

Answers (2)

balderman
balderman

Reputation: 23825

Below is a dataclass based solution

from dataclasses import dataclass
from typing import List
from dacite import from_dict



@dataclass
class Dog:
    name:str
    age:int

@dataclass
class Person:
    name: str
    dogs: List[Dog] = None


data1 = [{'name':'jack','dogs':[{'name':'bark','age':12},{'name':'jumpy','age':3}]}]
persons1: List[Person] = [from_dict(Person,entry) for entry in data1]
print(persons1)

data2 = [{'name':'jack'}]
persons2: List[Person] = [from_dict(Person,entry) for entry in data2]
print(persons2)

output

[Person(name='jack', dogs=[Dog(name='bark', age=12), Dog(name='jumpy', age=3)])]
[Person(name='jack', dogs=None)]

Upvotes: 1

Grismar
Grismar

Reputation: 31354

None is not a valid B. You should define b to be of type Optional[B]:

from typing import Optional
import attr
import cattr


@attr.s(auto_attribs=True)
class B(object):
    b: int = 0


@attr.s(auto_attribs=True)
class A(object):
    a: int = 0
    b: Optional[B] = None


x = A(1)
x_dict = attr.asdict(x)
cattr.structure(x_dict, A)

Upvotes: 2

Related Questions