Skip to content

Commit c85bf66

Browse files
feature: Support new DeserializeAs setting
1 parent 70587e2 commit c85bf66

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[[entries]]
2+
id = "5d31c345-35ef-40f4-bf02-bfca907f0517"
3+
type = "feature"
4+
description = "Support new `DeserializeAs` setting"
5+
author = "@NiklasRosenstein"

databind.json/src/databind/json/converters.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from databind.core.settings import (
1212
Alias,
1313
DateFormat,
14+
DeserializeAs,
1415
ExtraKeys,
1516
Precision,
1617
Remainder,
@@ -413,7 +414,12 @@ def _get_alias_setting(ctx: Context, field_name: str) -> Alias:
413414
return ctx.get_setting(Alias) or Alias(field_name)
414415

415416
def _get_schema(self, ctx: Context) -> Schema:
416-
datatype = _unwrap_annotated(ctx.datatype)
417+
deserialize_as = ctx.get_setting(DeserializeAs)
418+
if deserialize_as is not None:
419+
datatype = TypeHint(deserialize_as.type)
420+
else:
421+
datatype = _unwrap_annotated(ctx.datatype)
422+
417423
try:
418424
return self.convert_to_schema(datatype)
419425
except ValueError as exc:

databind.json/tests/test_converters.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from databind.core.mapper import ObjectMapper
1313
from databind.core.settings import ( # noqa: F401
1414
Alias,
15+
DeserializeAs,
1516
ExtraKeys,
1617
Flattened,
1718
Remainder,
@@ -379,6 +380,38 @@ class A:
379380
assert mapper.deserialize({"a": 1, "spam": 2}, A) == A(1, {"spam": 2})
380381

381382

383+
def test_deserialize_as() -> None:
384+
mapper = make_mapper([SchemaConverter(), PlainDatatypeConverter()])
385+
386+
@dataclasses.dataclass
387+
class A:
388+
a: int
389+
390+
@dataclasses.dataclass
391+
class B(A):
392+
a: int = 42
393+
394+
@dataclasses.dataclass
395+
class MyClass:
396+
a: te.Annotated[A, DeserializeAs(B)]
397+
398+
with pytest.raises(ConversionError) as excinfo:
399+
assert mapper.serialize(MyClass(A(1)), MyClass) == {"a": {"a": 1}}
400+
assert (
401+
str(excinfo.value)
402+
== """expected test_converters.test_deserialize_as.<locals>.B, got test_converters.test_deserialize_as.<locals>.A instead
403+
404+
Trace:
405+
$: TypeHint(test_converters.test_deserialize_as.<locals>.MyClass)
406+
.a: TypeHint(typing_extensions.Annotated[test_converters.test_deserialize_as.<locals>.A, DeserializeAs(type=<class 'test_converters.test_deserialize_as.<locals>.B'>, priority=<Priority.NORMAL: 1>)])"""
407+
)
408+
409+
assert mapper.serialize(MyClass(B(2)), MyClass) == {"a": {"a": 2}}
410+
assert mapper.deserialize({"a": {"a": 1}}, MyClass) == MyClass(B(1))
411+
assert mapper.deserialize({"a": {"a": 2}}, MyClass) == MyClass(B(2))
412+
assert mapper.deserialize({"a": {}}, MyClass) == MyClass(B(42))
413+
414+
382415
def test_deserialize_union_dataclass_subclass():
383416
"""Tests that a subclass of a dataclass marked as a union is deserialized as a dataclass."""
384417

0 commit comments

Comments
 (0)