lidatong / dataclasses-json

Easily serialize Data Classes to and from JSON
MIT License
1.36k stars 153 forks source link

from_json doesn't work on a class that contains a PEP 585 parameterized generic #404

Closed Barabas5532 closed 1 year ago

Barabas5532 commented 1 year ago

When running the attached test, the class defined like this is not deserialising correctly.

@dataclass_json
@dataclass
class E:
    a: list["A"]

The a member is left as a dict instead of getting turned into class A.

I spotted this note in the python docs:

Note

PEP 585 generic types such as list["SomeClass"] will not be implicitly transformed into list[ForwardRef("SomeClass")] and thus will not automatically resolve to list[SomeClass].

Indeed, adding ForwardRef does make it work again, so this might just be a limitation of python. Actually while looking into this, I found that this is already fixed in python 3.11: https://github.com/python/cpython/commit/b465b606049f6f7dd0711cb031fdaa251818741a. I'll create this issue anyway for anyone else that might encounter the same issue in earlier python releases.

Full test code below:

``` from dataclasses import dataclass from dataclasses_json import dataclass_json from typing import ForwardRef @dataclass_json @dataclass(frozen=True) class A: value: int @dataclass_json @dataclass class B: a: A @dataclass_json @dataclass class C: a: "A" @dataclass_json @dataclass class D: a: list[A] @dataclass_json @dataclass class E: a: list["A"] @dataclass_json @dataclass class F: a: list[ForwardRef("A")] if __name__ == "__main__": import unittest class Test(unittest.TestCase): def test_b(self) -> None: uut = B(A(0)) json = uut.to_json() parsed = B.from_json(json) self.assertEqual(type(parsed.a), A) def test_c(self) -> None: uut = C(A(0)) json = uut.to_json() parsed = C.from_json(json) self.assertEqual(type(parsed.a), A) def test_d(self) -> None: uut = D([A(0)]) json = uut.to_json() parsed = D.from_json(json) self.assertEqual(type(parsed.a[0]), A) def test_e(self) -> None: uut = E([A(0)]) json = uut.to_json() parsed = E.from_json(json) self.assertEqual(type(parsed.a[0]), A) def test_F(self) -> None: uut = F([A(0)]) json = uut.to_json() parsed = F.from_json(json) self.assertEqual(type(parsed.a[0]), A) unittest.main() ```