diff --git a/dataclasses_json/core.py b/dataclasses_json/core.py index 32868c07..fb7f0e6b 100644 --- a/dataclasses_json/core.py +++ b/dataclasses_json/core.py @@ -241,6 +241,10 @@ def _support_extended_types(field_type, field_value): res = (field_value if isinstance(field_value, UUID) else UUID(field_value)) + elif _issubclass_safe(field_type, (int, float, str, bool)): + res = (field_value + if isinstance(field_value, field_type) + else field_type(field_value)) else: res = field_value return res diff --git a/tests/test_builtins.py b/tests/test_builtins.py new file mode 100644 index 00000000..78eacfd7 --- /dev/null +++ b/tests/test_builtins.py @@ -0,0 +1,34 @@ +from dataclasses import dataclass +from decimal import Decimal +from typing import Optional + +from pytest import mark, param + + +from dataclasses_json import DataClassJsonMixin + + +@dataclass(frozen=True) +class DataClassWithBuiltins(DataClassJsonMixin): + actually_a_str: str + actually_an_int: int + actually_a_float: float + + +@mark.parametrize( + "model_dict, expected_model", + [ + param( + {"actually_a_str": "str", "actually_an_int": 42, "actually_a_float": 42.1}, + DataClassWithBuiltins(actually_a_str="str", actually_an_int=42, actually_a_float=42.1), + id="Happy case" + ), + param( + {"actually_a_str": "str", "actually_an_int": Decimal("42.1"), "actually_a_float": Decimal("42.1")}, + DataClassWithBuiltins(actually_a_str="str", actually_an_int=42, actually_a_float=42.1), + id="Decimal as int and float" + ), + ] +) +def test__DataClassWithBuiltins__from_dict(model_dict, expected_model): + assert DataClassWithBuiltins.from_dict(model_dict) == expected_model diff --git a/tests/test_invariants.py b/tests/test_invariants.py index 1fa5e986..cf0195bf 100644 --- a/tests/test_invariants.py +++ b/tests/test_invariants.py @@ -15,7 +15,7 @@ (DataClassWithTuple, tuples, tuple), (DataClassWithFrozenSet, frozensets, frozenset), (DataClassWithDeque, deques, deque), - (DataClassWithOptional, optionals, lambda x: x)] + (DataClassWithOptional, optionals, lambda x: x[0])] example_input = [1] diff --git a/tests/test_letter_case.py b/tests/test_letter_case.py index ccc1d45c..acc7d9e6 100644 --- a/tests/test_letter_case.py +++ b/tests/test_letter_case.py @@ -53,7 +53,7 @@ class FieldNamePerson: @dataclass class CamelCasePersonWithOverride: given_name: str - years_on_earth: str = field(metadata=config(field_name='age')) + years_on_earth: int = field(metadata=config(field_name='age')) class TestLetterCase: diff --git a/tests/test_str_subclass.py b/tests/test_str_subclass.py new file mode 100644 index 00000000..9979ca2a --- /dev/null +++ b/tests/test_str_subclass.py @@ -0,0 +1,32 @@ +from dataclasses import dataclass + +from dataclasses_json import DataClassJsonMixin + + +class MyStr(str): + + def is_even_length(self) -> bool: + return len(self) % 2 == 0 + + +@dataclass(frozen=True) +class DataClassWithStrSubclass(DataClassJsonMixin): + any_str: str + my_str: MyStr + + +class TestDataClassWithStrSubclass: + + def test_encode__no_instantiation_required(self): + model_dict = {"any_str": "str", "my_str": MyStr("str")} + expected = DataClassWithStrSubclass(any_str="str", my_str=MyStr("str")) + actual = DataClassWithStrSubclass.from_dict(model_dict) + assert expected == actual + assert model_dict["my_str"] is actual.my_str + + def test_encode__subclass_str_instantiated(self): + model_dict = {"any_str": "str", "my_str": "str"} + expected = DataClassWithStrSubclass(any_str="str", my_str=MyStr("str")) + actual = DataClassWithStrSubclass.from_dict(model_dict) + assert expected == actual + assert model_dict["my_str"] is not actual.my_str \ No newline at end of file