diff --git a/serde/de.py b/serde/de.py index be905d8b..7752fd56 100644 --- a/serde/de.py +++ b/serde/de.py @@ -123,13 +123,18 @@ def default_deserializer(_cls: Type[Any], obj: Any) -> Any: """ -def _get_by_aliases(d: Dict[str, str], aliases: List[str]) -> str: +def _get_by_aliases( + d: Dict[str, str], aliases: List[str], raise_error: bool = True +) -> Optional[str]: if not aliases: - raise KeyError("Tried all aliases, but key not found") + if raise_error: + raise KeyError("Tried all aliases, but key not found") + else: + return None if aliases[0] in d: return d[aliases[0]] else: - return _get_by_aliases(d, aliases[1:]) + return _get_by_aliases(d, aliases[1:], raise_error=raise_error) def _exists_by_aliases(d: Dict[str, str], aliases: List[str]) -> bool: @@ -804,7 +809,13 @@ def opt(self, arg: DeField[Any]) -> str: if arg.iterbased: exists = f"{arg.data} is not None" else: - exists = f'{arg.datavar}.get("{arg.conv_name()}") is not None' + name = arg.conv_name() + if arg.alias: + aliases = (f'"{s}"' for s in [name, *arg.alias]) + get = f"_get_by_aliases(data, [{','.join(aliases)}], raise_error=False)" + else: + get = f'{arg.datavar}.get("{name}")' + exists = f"{get} is not None" return f"({self.render(value_arg)}) if {exists} else None" def list(self, arg: DeField[Any]) -> str: diff --git a/tests/test_basics.py b/tests/test_basics.py index 41b67696..d3efd01c 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -573,6 +573,15 @@ class Foo: assert ff.a == 2 +def test_optional_and_alias(): + @serde.serde + class Foo: + a: Optional[int] = serde.field(alias=["b"]) + + assert Foo(1) == serde.json.from_json(Foo, '{"b":1}') + assert Foo(None) == serde.json.from_json(Foo, '{"c":1}') + + def test_default_and_rename(): @serde.serde class Foo: