diff --git a/dacite/core.py b/dacite/core.py index 9e45129..3fce8ab 100644 --- a/dacite/core.py +++ b/dacite/core.py @@ -96,7 +96,10 @@ def _build_value(type_: Type, data: Any, config: Config) -> Any: elif is_generic_collection(type_): data = _build_value_for_collection(collection=type_, data=data, config=config) elif cache(is_dataclass)(type_) and isinstance(data, Mapping): - data = from_dict(data_class=type_, data=data, config=config) + if hasattr(type_, "from_dict"): + data = type_.from_dict(data_class=type_, data=data, config=config) + else: + data = from_dict(data_class=type_, data=data, config=config) for cast_type in config.cast: if is_subclass(type_, cast_type): if is_generic_collection(type_): diff --git a/tests/core/test_config.py b/tests/core/test_config.py index 0422fda..829ddb4 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from datetime import date from enum import Enum from typing import Optional, List, Union @@ -225,3 +226,35 @@ class Z: result = from_dict(Z, data, Config(strict_unions_match=True)) assert result == Z(u=Y(f=1)) + + +def test_custom_from_dict_in_nested_data_class(): + @dataclass + class X: + d: date + t: str + + def from_dict(data_class, data, config): + data["t"] = "prefix {}".format(data["t"]) + return from_dict( + data_class=data_class, + data=data, + config=Config(type_hooks={date: date.fromtimestamp}), + ) + + @dataclass + class Y: + d: date + x: X + + config = Config(type_hooks={date: date.fromordinal}) + data = {"d": 737790, "x": {"d": 1607511900.985121, "t": "abc"}} + result = from_dict(Y, data, config=config) + + assert result == Y( + d=date(2020, 12, 31), + x=X( + d=date(2020, 12, 9), + t="prefix abc", + ), + )