python ^3.7. Trying to create nested dataclasses to work with complex json response. I managed to do that with creating dataclass for every level of json and using __post_init_
to set fields as objects of other dataclasses. However that creates a lot of boilerplate code and also, there is no annotation for nested objects.
This answer helped me getting closer to the solution using wrapper:
https://stackoverflow.com/a/51565863/8325015
However it does not solve it for cases where attribute is list of objects. some_attribute: List[SomeClass]
Here is example that resembles my data:
from dataclasses import dataclass, is_dataclass
from typing import List
from copy import deepcopy
# decorator from the linked thread:
def nested_deco(*args, **kwargs):
def wrapper(check_class):
# passing class to investigate
check_class = dataclass(check_class, **kwargs)
o_init = check_class.__init__
def __init__(self, *args, **kwargs):
for name, value in kwargs.items():
# getting field type
ft = check_class.__annotations__.get(name, None)
if is_dataclass(ft) and isinstance(value, dict):
obj = ft(**value)
kwargs[name] = obj
o_init(self, *args, **kwargs)
check_class.__init__ = __init__
return check_class
return wrapper(args[0]) if args else wrapper
#some dummy dataclasses to resemble my data structure
@dataclass
class IterationData:
question1: str
question2: str
@nested_deco
@dataclass
class IterationResult:
name: str
data: IterationData
@nested_deco
@dataclass
class IterationResults:
iterations: List[IterationResult]
@dataclass
class InstanceData:
date: str
owner: str
@nested_deco
@dataclass
class Instance:
data: InstanceData
name: str
@nested_deco
@dataclass
class Result:
status: str
iteration_results: IterationResults
@nested_deco
@dataclass
class MergedInstance:
instance: Instance
result: Result
#example data
single_instance = {
"instance": {
"name": "example1",
"data": {
"date": "2021-01-01",
"owner": "Maciek"
}
},
"result": {
"status": "complete",
"iteration_results": [
{
"name": "first",
"data": {
"question1": "yes",
"question2": "no"
}
}
]
}
}
instances = [deepcopy(single_instance) for i in range(3)] #created a list just to resemble mydata
objres = [MergedInstance(**inst) for inst in instances]
As you will notice. nested_deco
works perfectly for attributes of MergedInstance
and for attribute data
of Instance
but it does not load IterationResults
class on iteration_results
of Result
.
Is there a way to achieve it?
I attach also example with my post_init solution which creates objects of classes but there is no annotation of attributes:
@dataclass
class IterationData:
question1: str
question2: str
@dataclass
class IterationResult:
name: str
data: dict
def __post_init__(self):
self.data = IterationData(**self.data)
@dataclass
class InstanceData:
date: str
owner: str
@dataclass
class Instance:
data: dict
name: str
def __post_init__(self):
self.data = InstanceData(**self.data)
@dataclass
class Result:
status: str
iteration_results: list
def __post_init__(self):
self.iteration_results = [IterationResult(**res) for res in self.iteration_results]
@dataclass
class MergedInstance:
instance: dict
result: dict
def __post_init__(self):
self.instance = Instance(**self.instance)
self.result = Result(**self.result)