diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 268140cc2..6d9e4f1e5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/asottile/reorder_python_imports - rev: v2.3.6 + rev: v2.4.0 hooks: - id: reorder-python-imports - repo: https://github.com/ambv/black diff --git a/tests/fixtures/defxmlschema/chapter12.json b/tests/fixtures/defxmlschema/chapter12.json index 357f1313c..248d235b9 100644 --- a/tests/fixtures/defxmlschema/chapter12.json +++ b/tests/fixtures/defxmlschema/chapter12.json @@ -48,7 +48,8 @@ "other_attributes": { "{http://example.org/oth}custom": "12" } - } + }, + "substituted": false } ] } \ No newline at end of file diff --git a/tests/fixtures/defxmlschema/chapter17.json b/tests/fixtures/defxmlschema/chapter17.json index 857e12444..68b5372dd 100644 --- a/tests/fixtures/defxmlschema/chapter17.json +++ b/tests/fixtures/defxmlschema/chapter17.json @@ -22,7 +22,8 @@ "quantity": 1, "color": null, "number": 563 - } + }, + "substituted": false } ] }, diff --git a/tests/formats/dataclass/parsers/test_xml.py b/tests/formats/dataclass/parsers/test_xml.py index 62670c6f8..e3a0ac473 100644 --- a/tests/formats/dataclass/parsers/test_xml.py +++ b/tests/formats/dataclass/parsers/test_xml.py @@ -32,7 +32,7 @@ def test_end(self, mock_emit_event): objects = [] queue = [] var = XmlVar(text=True, name="foo", qname="foo", types=[bool]) - queue.append(PrimitiveNode(var=var, ns_map={})) + queue.append(PrimitiveNode.from_var(var, {})) result = self.parser.end(queue, objects, "enabled", "true", None) self.assertTrue(result) diff --git a/tests/formats/dataclass/serializers/test_xml.py b/tests/formats/dataclass/serializers/test_xml.py index acffa43a2..41e1ff405 100644 --- a/tests/formats/dataclass/serializers/test_xml.py +++ b/tests/formats/dataclass/serializers/test_xml.py @@ -234,7 +234,7 @@ def test_write_any_type_with_derived_element_primitive(self): def test_write_any_type_with_derived_element_dataclass(self): var = XmlVar(wildcard=True, qname="a", name="a") - value = DerivedElement(qname="a", value=BookForm(title="def")) + value = DerivedElement(qname="a", value=BookForm(title="def"), substituted=True) expected = [ (XmlWriterEvent.START, "a"), (XmlWriterEvent.ATTR, "lang", "en"), diff --git a/xsdata/formats/dataclass/models/generics.py b/xsdata/formats/dataclass/models/generics.py index ce76b762e..8bf359bd8 100644 --- a/xsdata/formats/dataclass/models/generics.py +++ b/xsdata/formats/dataclass/models/generics.py @@ -46,3 +46,4 @@ class DerivedElement(Generic[T]): qname: str value: T + substituted: bool = False diff --git a/xsdata/formats/dataclass/parsers/nodes.py b/xsdata/formats/dataclass/parsers/nodes.py index 94d7c5904..005b717b8 100644 --- a/xsdata/formats/dataclass/parsers/nodes.py +++ b/xsdata/formats/dataclass/parsers/nodes.py @@ -58,6 +58,7 @@ class ElementNode(XmlNode): position: int mixed: bool = False derived: bool = False + substituted: bool = False assigned: Set = field(default_factory=set) def bind(self, qname: str, text: NoneStr, tail: NoneStr, objects: List) -> bool: @@ -80,7 +81,7 @@ def bind(self, qname: str, text: NoneStr, tail: NoneStr, objects: List) -> bool: obj = self.meta.clazz(**params) if self.derived: - obj = DerivedElement(qname=qname, value=obj) + obj = DerivedElement(qname=qname, value=obj, substituted=self.substituted) objects.append((qname, obj)) @@ -119,27 +120,38 @@ def build_node( position=position, ) + xsi_type = ParserUtils.xsi_type(attrs, ns_map) + if var.clazz: return self.build_element_node( - var.clazz, attrs, ns_map, position, var.derived + var.clazz, + attrs, + ns_map, + position, + var.derived, + xsi_type, ) if not var.any_type and not var.wildcard: return PrimitiveNode.from_var(var, ns_map) - xsi_type = ParserUtils.xsi_type(attrs, ns_map) datatype = DataType.from_qname(xsi_type) if xsi_type else None derived = var.derived or var.wildcard - clazz = None - - if xsi_type and not datatype: - clazz = self.context.find_type(xsi_type) - if datatype: return PrimitiveNode.from_datatype(datatype, derived, ns_map) + node = None + clazz = None + if xsi_type: + clazz = self.context.find_type(xsi_type) + if clazz: - return self.build_element_node(clazz, attrs, ns_map, position, derived) + node = self.build_element_node( + clazz, attrs, ns_map, position, derived, xsi_type + ) + + if node: + return node return WildcardNode(var=var, attrs=attrs, ns_map=ns_map, position=position) @@ -166,12 +178,12 @@ def build_element_node( ns_map: Dict, position: int, derived: bool, + xsi_type: Optional[str] = None, ) -> Optional[XmlNode]: - xsi_type = ParserUtils.xsi_type(attrs, ns_map) - is_nillable = ParserUtils.is_nillable(attrs) + meta = self.context.fetch(clazz, self.meta.namespace, xsi_type) - if not is_nillable and meta.nillable: + if not meta or (meta.nillable and not ParserUtils.is_nillable(attrs)): return None return ElementNode( @@ -182,6 +194,7 @@ def build_element_node( context=self.context, position=position, derived=derived, + substituted=xsi_type is not None, mixed=self.meta.has_var(mode=FindMode.MIXED_CONTENT), ) diff --git a/xsdata/formats/dataclass/serializers/xml.py b/xsdata/formats/dataclass/serializers/xml.py index 8fa87ad1c..9a5c0105e 100644 --- a/xsdata/formats/dataclass/serializers/xml.py +++ b/xsdata/formats/dataclass/serializers/xml.py @@ -201,7 +201,7 @@ def write_derived_element( ): if is_dataclass(value.value): xsi_type = None - if not var.mixed: + if value.substituted: meta = self.context.build(value.value.__class__) xsi_type = QName(meta.source_qname)