diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cca18ba0c..f5633e427 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,20 +2,20 @@ exclude: tests/fixtures repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 + rev: v3.15.0 hooks: - id: pyupgrade args: [ --py38-plus ] - repo: https://github.com/asottile/reorder_python_imports - rev: v3.10.0 + rev: v3.12.0 hooks: - id: reorder-python-imports - repo: https://github.com/ambv/black - rev: 23.7.0 + rev: 23.11.0 hooks: - id: black - repo: https://github.com/PyCQA/autoflake - rev: v2.2.0 + rev: v2.2.1 hooks: - id: autoflake - repo: https://github.com/PyCQA/flake8 @@ -30,7 +30,7 @@ repos: args: [ "--suppress-none-returning" ] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -41,7 +41,7 @@ repos: - id: docformatter args: [ "--in-place", "--pre-summary-newline" ] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.5.1 + rev: v1.7.0 hooks: - id: mypy files: ^(xsdata/) diff --git a/tests/fixtures/compound/models.py b/tests/fixtures/compound/models.py index 5ba180738..04de69e4b 100644 --- a/tests/fixtures/compound/models.py +++ b/tests/fixtures/compound/models.py @@ -35,7 +35,7 @@ class Root: class Meta: name = "root" - alpha_or_bravo: List[Union[Bravo, Alpha]] = field( + alpha_or_bravo: List[Union[Alpha, Bravo]] = field( default_factory=list, metadata={ "type": "Elements", diff --git a/tests/formats/dataclass/parsers/test_json.py b/tests/formats/dataclass/parsers/test_json.py index 277b1a517..f15c23d46 100644 --- a/tests/formats/dataclass/parsers/test_json.py +++ b/tests/formats/dataclass/parsers/test_json.py @@ -1,6 +1,9 @@ import json +from dataclasses import asdict +from dataclasses import make_dataclass from typing import List from typing import Optional +from typing import Union from xml.etree.ElementTree import QName from tests import fixtures_dir @@ -370,6 +373,15 @@ def test_bind_any_type_with_derived_dataclass(self): self.assertEqual("Unable to locate xsi:type `notexists`", str(cm.exception)) + def test_bind_text_with_unions(self): + Fixture = make_dataclass("Fixture", [("x", List[Union[int, float, str, bool]])]) + values = ["foo", 12.2, "12.2", 12, "12", True, "false"] + data = json.dumps({"x": values}) + + result = self.parser.from_string(data, Fixture) + expected = ["foo", 12.2, 12.2, 12, 12, True, False] + self.assertEqual({"x": expected}, asdict(result)) + def test_find_var(self): meta = self.parser.context.build(TypeB) xml_vars = meta.get_all_vars() diff --git a/xsdata/formats/dataclass/parsers/json.py b/xsdata/formats/dataclass/parsers/json.py index f163c367a..1ea771c0f 100644 --- a/xsdata/formats/dataclass/parsers/json.py +++ b/xsdata/formats/dataclass/parsers/json.py @@ -16,6 +16,7 @@ from xsdata.exceptions import ParserError from xsdata.formats.bindings import AbstractParser from xsdata.formats.bindings import T +from xsdata.formats.converter import converter from xsdata.formats.dataclass.context import XmlContext from xsdata.formats.dataclass.models.elements import XmlMeta from xsdata.formats.dataclass.models.elements import XmlVar @@ -241,6 +242,8 @@ def bind_text(self, meta: XmlMeta, var: XmlVar, value: Any) -> Any: # field can support any object return the value as it is return value + value = converter.serialize(value) + # Convert value according to the field types return ParserUtils.parse_value( value=value, diff --git a/xsdata/formats/dataclass/serializers/mixins.py b/xsdata/formats/dataclass/serializers/mixins.py index 89a8fb8e7..ad31da6d7 100644 --- a/xsdata/formats/dataclass/serializers/mixins.py +++ b/xsdata/formats/dataclass/serializers/mixins.py @@ -213,7 +213,7 @@ def end_tag(self, qname: str): :param qname: Tag qualified name """ self.flush_start(True) - self.handler.endElementNS(split_qname(qname), None) + self.handler.endElementNS(split_qname(qname), "") if self.tail: self.handler.characters(self.tail) @@ -239,20 +239,22 @@ def flush_start(self, is_nil: bool = True): :param is_nil: If true add ``xsi:nil="true"`` to the element attributes """ - if self.pending_tag: - if not is_nil: - self.attrs.pop(XSI_NIL, None) + if not self.pending_tag: + return - for name in self.attrs.keys(): - self.add_namespace(name[0]) + if not is_nil: + self.attrs.pop(XSI_NIL, None) - self.reset_default_namespace() - self.start_namespaces() + for name in self.attrs.keys(): + self.add_namespace(name[0]) - self.handler.startElementNS(self.pending_tag, None, self.attrs) - self.attrs = {} - self.in_tail = False - self.pending_tag = None + self.reset_default_namespace() + self.start_namespaces() + + self.handler.startElementNS(self.pending_tag, "", self.attrs) # type: ignore + self.attrs = {} + self.in_tail = False + self.pending_tag = None def start_namespaces(self): """