diff --git a/tests/formats/dataclass/parsers/test_json.py b/tests/formats/dataclass/parsers/test_json.py index f3ce19ab2..da82f8387 100644 --- a/tests/formats/dataclass/parsers/test_json.py +++ b/tests/formats/dataclass/parsers/test_json.py @@ -81,7 +81,11 @@ def test_bind_dataclass_union(self): b = make_dataclass("b", [("x", int), ("y", str), ("z", float)]) c = make_dataclass("c", [("x", int)]) var = XmlVar( - element=True, name="union", qname="union", types=[a, b, c], dataclass=True + element=True, + name="union", + qname="union", + types=[a, b, c, int], + dataclass=True, ) data = {"x": 1, "y": "foo", "z": 1.0} @@ -89,6 +93,19 @@ def test_bind_dataclass_union(self): self.assertIsInstance(actual, b) + def test_bind_type_union(self): + a = make_dataclass("a", [("x", int), ("y", str)]) + var = XmlVar( + element=True, + name="union", + qname="union", + types=[a, int, float], + dataclass=True, + ) + + data = "1.1" + self.assertEqual(1.1, self.parser.bind_value(var, data)) + def test_bind_choice_simple(self): var = XmlVar( elements=True, diff --git a/xsdata/formats/dataclass/parsers/json.py b/xsdata/formats/dataclass/parsers/json.py index cdfa3d7c7..bb27550de 100644 --- a/xsdata/formats/dataclass/parsers/json.py +++ b/xsdata/formats/dataclass/parsers/json.py @@ -5,8 +5,10 @@ from dataclasses import dataclass from dataclasses import field from dataclasses import fields +from dataclasses import is_dataclass from typing import Any from typing import Dict +from typing import List from typing import Optional from typing import Type @@ -56,7 +58,10 @@ def bind_value(self, var: XmlVar, value: Any) -> Any: return dict(value) if var.is_clazz_union: - return self.bind_dataclass_union(value, var) + if isinstance(value, dict): + return self.bind_dataclass_union(value, var) + + return self.bind_type_union(value, var) if var.clazz: return self.bind_dataclass(value, var.clazz) @@ -67,7 +72,7 @@ def bind_value(self, var: XmlVar, value: Any) -> Any: if var.elements: return self.bind_choice(value, var) - return self.parse_value(value, var) + return self.parse_value(value, var.types, var.default, var.tokens) def bind_dataclass(self, data: Dict, clazz: Type[T]) -> T: """Recursively build the given model from the input dict data.""" @@ -93,6 +98,9 @@ def bind_dataclass_union(self, value: Dict, var: XmlVar) -> Any: obj = None max_score = -1.0 for clazz in var.types: + if not is_dataclass(clazz): + continue + candidate = self.bind_dataclass(value, clazz) score = ParserUtils.score_object(candidate) if score > max_score: @@ -101,6 +109,10 @@ def bind_dataclass_union(self, value: Dict, var: XmlVar) -> Any: return obj + def bind_type_union(self, value: Any, var: XmlVar) -> Any: + types = [tp for tp in var.types if not is_dataclass(tp)] + return self.parse_value(value, types, var.default, var.tokens) + def bind_wildcard(self, value: Any) -> Any: """Bind data to a wildcard model.""" if isinstance(value, Dict): @@ -165,10 +177,12 @@ def bind_choice_dataclass(self, value: Dict, var: XmlVar) -> Any: raise ParserError(f"XmlElements undefined choice: `{var.name}` for `{value}`") @classmethod - def parse_value(cls, value: Any, var: XmlVar) -> Any: + def parse_value( + cls, value: Any, types: List[Type], default: Any, tokens: bool + ) -> Any: """Convert any value to one of the given var types.""" return ParserUtils.parse_value( - value, var.types, var.default, ns_map=EMPTY_MAP, tokens=var.tokens + value, types, default, ns_map=EMPTY_MAP, tokens=tokens )