diff --git a/docs/models/fields.md b/docs/models/fields.md index 8c151c490..09f1fd5ef 100644 --- a/docs/models/fields.md +++ b/docs/models/fields.md @@ -63,12 +63,12 @@ The namespace name of the XML element or attribute. ### `nillable` Specify if the field has to be present in the serialized result even when it doesn't -have any meaningful content. +have any meaningful content. If the field is not required the serializer will ignore it. ```python >>> @dataclass ... class Root: -... first: Optional[str] = field(metadata={"nillable": True}, default=None) +... first: Optional[str] = field(metadata={"nillable": True, "required": True}, default=None) ... second: Optional[str] = field(default=None) ... >>> print(serializer.render(Root())) diff --git a/tests/fixtures/models.py b/tests/fixtures/models.py index 6987ccad2..b841ca17f 100644 --- a/tests/fixtures/models.py +++ b/tests/fixtures/models.py @@ -172,6 +172,8 @@ class SequentialType: x4: Optional[int] = field( default=None, metadata={"type": "Element", "sequence": 2} ) + x5: Optional[str] = field(default=None, metadata={"type": "Element", "nillable": True}) + x6: Optional[str] = field(default=None, metadata={"type": "Element", "nillable": True, "required": True}) @dataclass diff --git a/tests/formats/dataclass/serializers/test_mixins.py b/tests/formats/dataclass/serializers/test_mixins.py index 2eb2e8280..e79cd1c19 100644 --- a/tests/formats/dataclass/serializers/test_mixins.py +++ b/tests/formats/dataclass/serializers/test_mixins.py @@ -693,6 +693,10 @@ def test_convert_choice_with_derived_dataclass(self): expected = [ ("start", "a"), ("attr", "a0", "foo"), + ("start", "{xsdata}x6"), + ("attr", QNames.XSI_NIL, "true"), + ("data", None), + ("end", "{xsdata}x6"), ("end", "a"), ] @@ -814,6 +818,7 @@ def test_next_value(self): x2 = next(meta.find_children("x2")) x3 = next(meta.find_children("x3")) x4 = next(meta.find_children("x4")) + x6 = next(meta.find_children("x6")) actual = self.generator.next_value(obj, meta) expected = [ @@ -825,6 +830,7 @@ def test_next_value(self): (x1, 4), (x3, 9), (x4, 10), + (x6, None), ] self.assertIsInstance(actual, Generator) diff --git a/xsdata/formats/dataclass/serializers/mixins.py b/xsdata/formats/dataclass/serializers/mixins.py index da3a0f47e..3e7c31fc5 100644 --- a/xsdata/formats/dataclass/serializers/mixins.py +++ b/xsdata/formats/dataclass/serializers/mixins.py @@ -933,7 +933,7 @@ def next_value(cls, obj: Any, meta: XmlMeta) -> Iterator[Tuple[XmlVar, Any]]: if var.sequence is None: value = getattr(obj, var.name) - if value is not None or var.nillable: + if value is not None or (var.nillable and var.required): yield var, value index += 1 continue @@ -955,11 +955,11 @@ def next_value(cls, obj: Any, meta: XmlMeta) -> Iterator[Tuple[XmlVar, Any]]: if j < len(values): rolling = True value = values[j] - if value is not None or var.nillable: + if value is not None or (var.nillable and var.required): yield var, value elif j == 0: rolling = True - if values is not None or var.nillable: + if values is not None or (var.nillable and var.required): yield var, values j += 1