Skip to content

xml

xsdata.formats.dataclass.serializers.xml

XmlSerializer dataclass

Bases: AbstractSerializer

Xml serializer for data classes.

Parameters:

Name Type Description Default
config SerializerConfig

The serializer config instance

field(default_factory=SerializerConfig)
context XmlContext

The models context instance

field(default_factory=XmlContext)
writer Type[XmlWriter]

The xml writer class

field(default=default_writer())
Source code in xsdata/formats/dataclass/serializers/xml.py
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
@dataclass
class XmlSerializer(AbstractSerializer):
    """Xml serializer for data classes.

    Args:
        config: The serializer config instance
        context: The models context instance
        writer: The xml writer class
    """

    config: SerializerConfig = field(default_factory=SerializerConfig)
    context: XmlContext = field(default_factory=XmlContext)
    writer: Type[XmlWriter] = field(default=default_writer())

    def render(self, obj: Any, ns_map: Optional[Dict] = None) -> str:
        """Serialize the input model instance to xml string.

        Args:
            obj: The input model instance to serialize
            ns_map: A user defined namespace prefix-URI map

        Returns:
            The serialized xml string output.
        """
        output = StringIO()
        self.write(output, obj, ns_map)
        return output.getvalue()

    def write(self, out: TextIO, obj: Any, ns_map: Optional[Dict] = None):
        """Serialize the given object to the output text stream.

        Args:
            out: The output text stream
            obj: The input model instance to serialize
            ns_map: A user defined namespace prefix-URI map
        """
        events = self.write_object(obj)
        handler = self.writer(
            config=self.config,
            output=out,
            ns_map=namespaces.clean_prefixes(ns_map) if ns_map else {},
        )
        handler.write(events)

    def write_object(self, obj: Any) -> EventIterator:
        """Convert a user model, or derived element instance to sax events.

        Args:
            obj: A user model, or derived element instance

        Yields:
            An iterator of sax events.
        """
        qname = xsi_type = None
        if isinstance(obj, self.context.class_type.derived_element):
            meta = self.context.build(
                obj.value.__class__, globalns=self.config.globalns
            )
            qname = obj.qname
            obj = obj.value
            xsi_type = self.real_xsi_type(qname, meta.target_qname)

        yield from self.write_dataclass(obj, qname=qname, xsi_type=xsi_type)

    def write_dataclass(
        self,
        obj: Any,
        namespace: Optional[str] = None,
        qname: Optional[str] = None,
        nillable: bool = False,
        xsi_type: Optional[str] = None,
    ) -> EventIterator:
        """Convert a model instance to sax events.

        Optionally override the qualified name and the
        xsi attributes type and nil.

        Args:
            obj: A model instance
            namespace: The field namespace URI
            qname: Override the field qualified name
            nillable: Specifies whether the field is nillable
            xsi_type: Override the field xsi type

        Yields:
            An iterator of sax events.
        """
        meta = self.context.build(
            obj.__class__,
            namespace,
            globalns=self.config.globalns,
        )
        qname = qname or meta.qname
        nillable = nillable or meta.nillable
        namespace, tag = namespaces.split_qname(qname)

        yield XmlWriterEvent.START, qname

        for key, value in self.next_attribute(
            obj, meta, nillable, xsi_type, self.config.ignore_default_attributes
        ):
            yield XmlWriterEvent.ATTR, key, value

        for var, value in self.next_value(obj, meta):
            yield from self.write_value(value, var, namespace)

        yield XmlWriterEvent.END, qname

    def write_xsi_type(
        self,
        value: Any,
        var: XmlVar,
        namespace: Optional[str],
    ) -> EventIterator:
        """Convert a xsi:type value to sax events.

        The value can be assigned to wildcard, element or compound fields

        Args:
            value: A model instance
            var: The field metadata instance
            namespace: The field namespace URI

        Yields:
            An iterator of sax events.
        """
        if var.is_wildcard:
            choice = var.find_value_choice(value, True)
            if choice:
                yield from self.write_value(value, choice, namespace)
            else:
                yield from self.write_dataclass(value, namespace)
        elif var.is_element:
            xsi_type = self.xsi_type(var, value, namespace)
            yield from self.write_dataclass(
                value,
                namespace,
                var.qname,
                var.nillable,
                xsi_type,
            )
        else:
            # var elements/compound
            meta = self.context.fetch(value.__class__, namespace)
            yield from self.write_dataclass(value, qname=meta.target_qname)

    def write_value(
        self, value: Any, var: XmlVar, namespace: Optional[str]
    ) -> EventIterator:
        """Convert any value to sax events according to the var instance.

        The order of the checks is important as more than one condition
        can be true.

        Args:
            value: The input value
            var: The field metadata instance
            namespace: The class namespace URI

        Yields:
            An iterator of sax events.
        """
        if var.mixed:
            yield from self.write_mixed_content(value, var, namespace)
        elif var.is_text:
            yield from self.write_data(value, var)
        elif var.tokens:
            yield from self.write_tokens(value, var, namespace)
        elif var.is_elements:
            yield from self.write_elements(value, var, namespace)
        elif var.list_element and collections.is_array(value):
            yield from self.write_list(value, var, namespace)
        else:
            yield from self.write_any_type(value, var, namespace)

    def write_list(
        self,
        values: Iterable,
        var: XmlVar,
        namespace: Optional[str],
    ) -> EventIterator:
        """Convert an array of values to sax events.

        Args:
            values: A list, set, tuple instance
            var: The field metadata instance
            namespace: The class namespace

        Yields:
            An iterator of sax events.
        """
        if var.wrapper is not None:
            yield XmlWriterEvent.START, var.wrapper
            for value in values:
                yield from self.write_value(value, var, namespace)
            yield XmlWriterEvent.END, var.wrapper
        else:
            for value in values:
                yield from self.write_value(value, var, namespace)

    def write_tokens(
        self, value: Any, var: XmlVar, namespace: Optional[str]
    ) -> EventIterator:
        """Convert an array of token values to sax events.

        Args:
            value: A list, set, tuple instance
            var: The field metadata instance
            namespace: The class namespace

        Yields:
            An iterator of sax events.
        """
        if value or var.nillable or var.required:
            if value and collections.is_array(value[0]):
                for val in value:
                    yield from self.write_element(val, var, namespace)
            else:
                yield from self.write_element(value, var, namespace)

    def write_mixed_content(
        self,
        values: List,
        var: XmlVar,
        namespace: Optional[str],
    ) -> EventIterator:
        """Convert mixed content values to sax events.

        Args:
            values: A list instance of mixed type values
            var: The field metadata instance
            namespace: The class namespace

        Yields:
            An iterator of sax events.
        """
        for value in values:
            yield from self.write_any_type(value, var, namespace)

    def write_any_type(
        self, value: Any, var: XmlVar, namespace: Optional[str]
    ) -> EventIterator:
        """Convert a value assigned to a xs:anyType field to sax events.

        Args:
            value: A list instance of mixed type values
            var: The field metadata instance
            namespace: The class namespace

        Yields:
            An iterator of sax events.
        """
        if isinstance(value, self.context.class_type.any_element):
            yield from self.write_any_element(value, var, namespace)
        elif isinstance(value, self.context.class_type.derived_element):
            yield from self.write_derived_element(value, namespace)
        elif self.context.class_type.is_model(value):
            yield from self.write_xsi_type(value, var, namespace)
        elif var.is_element:
            yield from self.write_element(value, var, namespace)
        else:
            yield from self.write_data(value, var)

    def write_derived_element(
        self, value: Any, namespace: Optional[str]
    ) -> EventIterator:
        """Convert a derived element instance to sax events.

        Args:
            value: A list instance of mixed type values
            namespace: The class namespace

        Yields:
            An iterator of sax events.
        """
        if self.context.class_type.is_model(value.value):
            meta = self.context.fetch(value.value.__class__)
            qname = value.qname
            xsi_type = self.real_xsi_type(qname, meta.target_qname)

            yield from self.write_dataclass(
                value.value, namespace, qname=qname, xsi_type=xsi_type
            )
        else:
            datatype = DataType.from_value(value.value)

            yield XmlWriterEvent.START, value.qname
            yield XmlWriterEvent.ATTR, QNames.XSI_TYPE, QName(str(datatype))
            yield XmlWriterEvent.DATA, value.value
            yield XmlWriterEvent.END, value.qname

    def write_any_element(
        self, value: Any, var: XmlVar, namespace: Optional[str]
    ) -> EventIterator:
        """Convert a generic any element instance to sax events.

        Args:
            value: A list instance of mixed type values
            var: The field metadata instance
            namespace: The class namespace

        Yields:
            An iterator of sax events.
        """
        if value.qname:
            namespace, tag = namespaces.split_qname(value.qname)
            yield XmlWriterEvent.START, value.qname

        for key, val in value.attributes.items():
            yield XmlWriterEvent.ATTR, key, val

        yield XmlWriterEvent.DATA, value.text

        for child in value.children:
            yield from self.write_any_type(child, var, namespace)

        if value.qname:
            yield XmlWriterEvent.END, value.qname

        if value.tail:
            yield XmlWriterEvent.DATA, value.tail

    def xsi_type(
        self, var: XmlVar, value: Any, namespace: Optional[str]
    ) -> Optional[str]:
        """Return the xsi:type for the given value and field metadata instance.

        If the value type is either a child or parent for one of the var types,
        we need to declare it as n xsi:type.

        Args:
            value: A list instance of mixed type values
            var: The field metadata instance
            namespace: The class namespace

        Raises:
            SerializerError: If the value type is completely unrelated to
                the field types.
        """
        if not value or value.__class__ in var.types:
            return None

        clazz = var.clazz
        if clazz is None or self.context.is_derived(value, clazz):
            meta = self.context.fetch(value.__class__, namespace)
            return self.real_xsi_type(var.qname, meta.target_qname)

        raise SerializerError(
            f"{value.__class__.__name__} is not derived from {clazz.__name__}"
        )

    def write_elements(
        self, value: Any, var: XmlVar, namespace: Optional[str]
    ) -> EventIterator:
        """Convert the value assigned to a compound field to sax events.

        Args:
            value: A list instance of mixed type values
            var: The field metadata instance
            namespace: The class namespace

        Yields:
            An iterator of sax events.
        """
        if collections.is_array(value):
            for val in value:
                yield from self.write_choice(val, var, namespace)
        else:
            yield from self.write_choice(value, var, namespace)

    def write_choice(
        self, value: Any, var: XmlVar, namespace: Optional[str]
    ) -> EventIterator:
        """Convert a single value assigned to a compound field to sax events.

        Args:
            value: A list instance of mixed type values
            var: The field metadata instance
            namespace: The class namespace

        Yields:
            An iterator of sax events.

        Raises:
            SerializerError: If the value doesn't match any choice field.
        """
        if isinstance(value, self.context.class_type.derived_element):
            choice = var.find_choice(value.qname)
            value = value.value

            if self.context.class_type.is_model(value):
                func = self.write_xsi_type
            else:
                func = self.write_element

        elif isinstance(value, self.context.class_type.any_element) and value.qname:
            choice = var.find_choice(value.qname)
            func = self.write_any_type
        else:
            check_subclass = self.context.class_type.is_model(value)
            choice = var.find_value_choice(value, check_subclass)
            func = self.write_value

            if not choice and check_subclass:
                func = self.write_xsi_type
                choice = var

        if not choice:
            raise SerializerError(
                f"XmlElements undefined choice: `{var.name}` for `{type(value)}`"
            )

        yield from func(value, choice, namespace)

    def write_element(
        self,
        value: Any,
        var: XmlVar,
        namespace: Optional[str],
    ) -> EventIterator:
        """Convert a value assigned to an element field to sax events.

        Args:
            value: A list instance of mixed type values
            var: The field metadata instance
            namespace: The class namespace (unused)

        Yields:
            An iterator of sax events.
        """
        yield XmlWriterEvent.START, var.qname

        if var.nillable:
            yield XmlWriterEvent.ATTR, QNames.XSI_NIL, "true"

        if value is not None and value != "" and var.any_type:
            datatype = DataType.from_value(value)
            if datatype != DataType.STRING:
                yield XmlWriterEvent.ATTR, QNames.XSI_TYPE, QName(str(datatype))

        yield XmlWriterEvent.DATA, self.encode(value, var)
        yield XmlWriterEvent.END, var.qname

    @classmethod
    def write_data(cls, value: Any, var: XmlVar) -> EventIterator:
        """Convert a value assigned to a text field to sax events.

        Args:
            value: A list instance of mixed type values
            var: The field metadata instance

        Yields:
            An iterator of sax events.
        """
        yield XmlWriterEvent.DATA, cls.encode(value, var)

    @classmethod
    def next_value(cls, obj: Any, meta: XmlMeta) -> Iterator[Tuple[XmlVar, Any]]:
        """Produce the next non attribute value of a model instance to convert.

        The generator will produce the values in the order the fields
        are defined in the model or by their sequence number.

        Sequential fields need to be rendered together in parallel order
        eg: <a1/><a2/><a1/><a/2></a1>

        Args:
            obj: The input model instance
            meta: The model metadata instance

        Yields:
            An iterator of field metadata instance and value tuples.
        """
        index = 0
        attrs = meta.get_element_vars()
        stop = len(attrs)
        while index < stop:
            var = attrs[index]

            if var.sequence is None:
                value = getattr(obj, var.name)
                if value is not None or var.nillable:
                    yield var, value
                index += 1
                continue

            indices = range(index, stop)
            end = next(
                i for i in indices[::-1] if attrs[i].sequence == var.sequence
            )  # pragma: no cover
            sequence = attrs[index : end + 1]
            index = end + 1
            j = 0

            rolling = True
            while rolling:
                rolling = False
                for var in sequence:
                    values = getattr(obj, var.name)
                    if collections.is_array(values):
                        if j < len(values):
                            rolling = True
                            value = values[j]
                            if value is not None or var.nillable:
                                yield var, value
                    elif j == 0:
                        rolling = True
                        if values is not None or var.nillable:
                            yield var, values

                j += 1

    @classmethod
    def next_attribute(
        cls,
        obj: Any,
        meta: XmlMeta,
        nillable: bool,
        xsi_type: Optional[str],
        ignore_optionals: bool,
    ) -> Iterator[Tuple[str, Any]]:
        """Produce the next attribute value to convert.

        Args:
            obj: The input model instance
            meta: The model metadata instance
            nillable: Specifies if the current element supports nillable content
            xsi_type: The real xsi:type of the object
            ignore_optionals: Specifies if optional attributes with default
                values should be ignored.

        Yields:
            An iterator of attribute name-value pairs.
        """
        for var in meta.get_attribute_vars():
            if var.is_attribute:
                value = getattr(obj, var.name)
                if (
                    value is None
                    or (collections.is_array(value) and not value)
                    or (ignore_optionals and var.is_optional(value))
                ):
                    continue

                yield var.qname, cls.encode(value, var)
            else:
                yield from getattr(obj, var.name, EMPTY_MAP).items()

        if xsi_type:
            yield QNames.XSI_TYPE, QName(xsi_type)

        if nillable:
            yield QNames.XSI_NIL, "true"

    @classmethod
    def encode(cls, value: Any, var: XmlVar) -> Any:
        """Encode a value for xml serialization.

        Converts values to strings. QName instances is an exception,
        those values need to wait until the XmlWriter assigns prefixes
        to namespaces per element node. Enums and Tokens may contain
        QName(s) so they also get a special treatment.

        We can't do all the conversions in the writer because we would
        need to carry the xml vars inside the writer. Instead of that we
        do the easy encoding here and leave the qualified names for
        later.

        Args:
            value: The simple type vale to encode
            var: The field metadata instance

        Returns:
            The encoded value.
        """
        if isinstance(value, (str, QName)) or var is None:
            return value

        if collections.is_array(value):
            return [cls.encode(v, var) for v in value]

        if isinstance(value, Enum):
            return cls.encode(value.value, var)

        return converter.serialize(value, format=var.format)

    @classmethod
    def real_xsi_type(cls, qname: str, target_qname: Optional[str]) -> Optional[str]:
        """Compare the qname with the target qname and return the real xsi:type.

        Args:
            qname: The field type qualified name
            target_qname: The value type qualified name

        Returns:
            None if the qname and target qname match, otherwise
            return the target qname.
        """
        return target_qname if target_qname != qname else None

render(obj, ns_map=None)

Serialize the input model instance to xml string.

Parameters:

Name Type Description Default
obj Any

The input model instance to serialize

required
ns_map Optional[Dict]

A user defined namespace prefix-URI map

None

Returns:

Type Description
str

The serialized xml string output.

Source code in xsdata/formats/dataclass/serializers/xml.py
48
49
50
51
52
53
54
55
56
57
58
59
60
def render(self, obj: Any, ns_map: Optional[Dict] = None) -> str:
    """Serialize the input model instance to xml string.

    Args:
        obj: The input model instance to serialize
        ns_map: A user defined namespace prefix-URI map

    Returns:
        The serialized xml string output.
    """
    output = StringIO()
    self.write(output, obj, ns_map)
    return output.getvalue()

write(out, obj, ns_map=None)

Serialize the given object to the output text stream.

Parameters:

Name Type Description Default
out TextIO

The output text stream

required
obj Any

The input model instance to serialize

required
ns_map Optional[Dict]

A user defined namespace prefix-URI map

None
Source code in xsdata/formats/dataclass/serializers/xml.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def write(self, out: TextIO, obj: Any, ns_map: Optional[Dict] = None):
    """Serialize the given object to the output text stream.

    Args:
        out: The output text stream
        obj: The input model instance to serialize
        ns_map: A user defined namespace prefix-URI map
    """
    events = self.write_object(obj)
    handler = self.writer(
        config=self.config,
        output=out,
        ns_map=namespaces.clean_prefixes(ns_map) if ns_map else {},
    )
    handler.write(events)

write_object(obj)

Convert a user model, or derived element instance to sax events.

Parameters:

Name Type Description Default
obj Any

A user model, or derived element instance

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def write_object(self, obj: Any) -> EventIterator:
    """Convert a user model, or derived element instance to sax events.

    Args:
        obj: A user model, or derived element instance

    Yields:
        An iterator of sax events.
    """
    qname = xsi_type = None
    if isinstance(obj, self.context.class_type.derived_element):
        meta = self.context.build(
            obj.value.__class__, globalns=self.config.globalns
        )
        qname = obj.qname
        obj = obj.value
        xsi_type = self.real_xsi_type(qname, meta.target_qname)

    yield from self.write_dataclass(obj, qname=qname, xsi_type=xsi_type)

write_dataclass(obj, namespace=None, qname=None, nillable=False, xsi_type=None)

Convert a model instance to sax events.

Optionally override the qualified name and the xsi attributes type and nil.

Parameters:

Name Type Description Default
obj Any

A model instance

required
namespace Optional[str]

The field namespace URI

None
qname Optional[str]

Override the field qualified name

None
nillable bool

Specifies whether the field is nillable

False
xsi_type Optional[str]

Override the field xsi type

None

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def write_dataclass(
    self,
    obj: Any,
    namespace: Optional[str] = None,
    qname: Optional[str] = None,
    nillable: bool = False,
    xsi_type: Optional[str] = None,
) -> EventIterator:
    """Convert a model instance to sax events.

    Optionally override the qualified name and the
    xsi attributes type and nil.

    Args:
        obj: A model instance
        namespace: The field namespace URI
        qname: Override the field qualified name
        nillable: Specifies whether the field is nillable
        xsi_type: Override the field xsi type

    Yields:
        An iterator of sax events.
    """
    meta = self.context.build(
        obj.__class__,
        namespace,
        globalns=self.config.globalns,
    )
    qname = qname or meta.qname
    nillable = nillable or meta.nillable
    namespace, tag = namespaces.split_qname(qname)

    yield XmlWriterEvent.START, qname

    for key, value in self.next_attribute(
        obj, meta, nillable, xsi_type, self.config.ignore_default_attributes
    ):
        yield XmlWriterEvent.ATTR, key, value

    for var, value in self.next_value(obj, meta):
        yield from self.write_value(value, var, namespace)

    yield XmlWriterEvent.END, qname

write_xsi_type(value, var, namespace)

Convert a xsi:type value to sax events.

The value can be assigned to wildcard, element or compound fields

Parameters:

Name Type Description Default
value Any

A model instance

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The field namespace URI

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
def write_xsi_type(
    self,
    value: Any,
    var: XmlVar,
    namespace: Optional[str],
) -> EventIterator:
    """Convert a xsi:type value to sax events.

    The value can be assigned to wildcard, element or compound fields

    Args:
        value: A model instance
        var: The field metadata instance
        namespace: The field namespace URI

    Yields:
        An iterator of sax events.
    """
    if var.is_wildcard:
        choice = var.find_value_choice(value, True)
        if choice:
            yield from self.write_value(value, choice, namespace)
        else:
            yield from self.write_dataclass(value, namespace)
    elif var.is_element:
        xsi_type = self.xsi_type(var, value, namespace)
        yield from self.write_dataclass(
            value,
            namespace,
            var.qname,
            var.nillable,
            xsi_type,
        )
    else:
        # var elements/compound
        meta = self.context.fetch(value.__class__, namespace)
        yield from self.write_dataclass(value, qname=meta.target_qname)

write_value(value, var, namespace)

Convert any value to sax events according to the var instance.

The order of the checks is important as more than one condition can be true.

Parameters:

Name Type Description Default
value Any

The input value

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The class namespace URI

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def write_value(
    self, value: Any, var: XmlVar, namespace: Optional[str]
) -> EventIterator:
    """Convert any value to sax events according to the var instance.

    The order of the checks is important as more than one condition
    can be true.

    Args:
        value: The input value
        var: The field metadata instance
        namespace: The class namespace URI

    Yields:
        An iterator of sax events.
    """
    if var.mixed:
        yield from self.write_mixed_content(value, var, namespace)
    elif var.is_text:
        yield from self.write_data(value, var)
    elif var.tokens:
        yield from self.write_tokens(value, var, namespace)
    elif var.is_elements:
        yield from self.write_elements(value, var, namespace)
    elif var.list_element and collections.is_array(value):
        yield from self.write_list(value, var, namespace)
    else:
        yield from self.write_any_type(value, var, namespace)

write_list(values, var, namespace)

Convert an array of values to sax events.

Parameters:

Name Type Description Default
values Iterable

A list, set, tuple instance

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The class namespace

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
def write_list(
    self,
    values: Iterable,
    var: XmlVar,
    namespace: Optional[str],
) -> EventIterator:
    """Convert an array of values to sax events.

    Args:
        values: A list, set, tuple instance
        var: The field metadata instance
        namespace: The class namespace

    Yields:
        An iterator of sax events.
    """
    if var.wrapper is not None:
        yield XmlWriterEvent.START, var.wrapper
        for value in values:
            yield from self.write_value(value, var, namespace)
        yield XmlWriterEvent.END, var.wrapper
    else:
        for value in values:
            yield from self.write_value(value, var, namespace)

write_tokens(value, var, namespace)

Convert an array of token values to sax events.

Parameters:

Name Type Description Default
value Any

A list, set, tuple instance

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The class namespace

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
def write_tokens(
    self, value: Any, var: XmlVar, namespace: Optional[str]
) -> EventIterator:
    """Convert an array of token values to sax events.

    Args:
        value: A list, set, tuple instance
        var: The field metadata instance
        namespace: The class namespace

    Yields:
        An iterator of sax events.
    """
    if value or var.nillable or var.required:
        if value and collections.is_array(value[0]):
            for val in value:
                yield from self.write_element(val, var, namespace)
        else:
            yield from self.write_element(value, var, namespace)

write_mixed_content(values, var, namespace)

Convert mixed content values to sax events.

Parameters:

Name Type Description Default
values List

A list instance of mixed type values

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The class namespace

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
def write_mixed_content(
    self,
    values: List,
    var: XmlVar,
    namespace: Optional[str],
) -> EventIterator:
    """Convert mixed content values to sax events.

    Args:
        values: A list instance of mixed type values
        var: The field metadata instance
        namespace: The class namespace

    Yields:
        An iterator of sax events.
    """
    for value in values:
        yield from self.write_any_type(value, var, namespace)

write_any_type(value, var, namespace)

Convert a value assigned to a xs:anyType field to sax events.

Parameters:

Name Type Description Default
value Any

A list instance of mixed type values

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The class namespace

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
def write_any_type(
    self, value: Any, var: XmlVar, namespace: Optional[str]
) -> EventIterator:
    """Convert a value assigned to a xs:anyType field to sax events.

    Args:
        value: A list instance of mixed type values
        var: The field metadata instance
        namespace: The class namespace

    Yields:
        An iterator of sax events.
    """
    if isinstance(value, self.context.class_type.any_element):
        yield from self.write_any_element(value, var, namespace)
    elif isinstance(value, self.context.class_type.derived_element):
        yield from self.write_derived_element(value, namespace)
    elif self.context.class_type.is_model(value):
        yield from self.write_xsi_type(value, var, namespace)
    elif var.is_element:
        yield from self.write_element(value, var, namespace)
    else:
        yield from self.write_data(value, var)

write_derived_element(value, namespace)

Convert a derived element instance to sax events.

Parameters:

Name Type Description Default
value Any

A list instance of mixed type values

required
namespace Optional[str]

The class namespace

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
def write_derived_element(
    self, value: Any, namespace: Optional[str]
) -> EventIterator:
    """Convert a derived element instance to sax events.

    Args:
        value: A list instance of mixed type values
        namespace: The class namespace

    Yields:
        An iterator of sax events.
    """
    if self.context.class_type.is_model(value.value):
        meta = self.context.fetch(value.value.__class__)
        qname = value.qname
        xsi_type = self.real_xsi_type(qname, meta.target_qname)

        yield from self.write_dataclass(
            value.value, namespace, qname=qname, xsi_type=xsi_type
        )
    else:
        datatype = DataType.from_value(value.value)

        yield XmlWriterEvent.START, value.qname
        yield XmlWriterEvent.ATTR, QNames.XSI_TYPE, QName(str(datatype))
        yield XmlWriterEvent.DATA, value.value
        yield XmlWriterEvent.END, value.qname

write_any_element(value, var, namespace)

Convert a generic any element instance to sax events.

Parameters:

Name Type Description Default
value Any

A list instance of mixed type values

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The class namespace

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
def write_any_element(
    self, value: Any, var: XmlVar, namespace: Optional[str]
) -> EventIterator:
    """Convert a generic any element instance to sax events.

    Args:
        value: A list instance of mixed type values
        var: The field metadata instance
        namespace: The class namespace

    Yields:
        An iterator of sax events.
    """
    if value.qname:
        namespace, tag = namespaces.split_qname(value.qname)
        yield XmlWriterEvent.START, value.qname

    for key, val in value.attributes.items():
        yield XmlWriterEvent.ATTR, key, val

    yield XmlWriterEvent.DATA, value.text

    for child in value.children:
        yield from self.write_any_type(child, var, namespace)

    if value.qname:
        yield XmlWriterEvent.END, value.qname

    if value.tail:
        yield XmlWriterEvent.DATA, value.tail

xsi_type(var, value, namespace)

Return the xsi:type for the given value and field metadata instance.

If the value type is either a child or parent for one of the var types, we need to declare it as n xsi:type.

Parameters:

Name Type Description Default
value Any

A list instance of mixed type values

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The class namespace

required

Raises:

Type Description
SerializerError

If the value type is completely unrelated to the field types.

Source code in xsdata/formats/dataclass/serializers/xml.py
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
def xsi_type(
    self, var: XmlVar, value: Any, namespace: Optional[str]
) -> Optional[str]:
    """Return the xsi:type for the given value and field metadata instance.

    If the value type is either a child or parent for one of the var types,
    we need to declare it as n xsi:type.

    Args:
        value: A list instance of mixed type values
        var: The field metadata instance
        namespace: The class namespace

    Raises:
        SerializerError: If the value type is completely unrelated to
            the field types.
    """
    if not value or value.__class__ in var.types:
        return None

    clazz = var.clazz
    if clazz is None or self.context.is_derived(value, clazz):
        meta = self.context.fetch(value.__class__, namespace)
        return self.real_xsi_type(var.qname, meta.target_qname)

    raise SerializerError(
        f"{value.__class__.__name__} is not derived from {clazz.__name__}"
    )

write_elements(value, var, namespace)

Convert the value assigned to a compound field to sax events.

Parameters:

Name Type Description Default
value Any

A list instance of mixed type values

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The class namespace

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
def write_elements(
    self, value: Any, var: XmlVar, namespace: Optional[str]
) -> EventIterator:
    """Convert the value assigned to a compound field to sax events.

    Args:
        value: A list instance of mixed type values
        var: The field metadata instance
        namespace: The class namespace

    Yields:
        An iterator of sax events.
    """
    if collections.is_array(value):
        for val in value:
            yield from self.write_choice(val, var, namespace)
    else:
        yield from self.write_choice(value, var, namespace)

write_choice(value, var, namespace)

Convert a single value assigned to a compound field to sax events.

Parameters:

Name Type Description Default
value Any

A list instance of mixed type values

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The class namespace

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Raises:

Type Description
SerializerError

If the value doesn't match any choice field.

Source code in xsdata/formats/dataclass/serializers/xml.py
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
def write_choice(
    self, value: Any, var: XmlVar, namespace: Optional[str]
) -> EventIterator:
    """Convert a single value assigned to a compound field to sax events.

    Args:
        value: A list instance of mixed type values
        var: The field metadata instance
        namespace: The class namespace

    Yields:
        An iterator of sax events.

    Raises:
        SerializerError: If the value doesn't match any choice field.
    """
    if isinstance(value, self.context.class_type.derived_element):
        choice = var.find_choice(value.qname)
        value = value.value

        if self.context.class_type.is_model(value):
            func = self.write_xsi_type
        else:
            func = self.write_element

    elif isinstance(value, self.context.class_type.any_element) and value.qname:
        choice = var.find_choice(value.qname)
        func = self.write_any_type
    else:
        check_subclass = self.context.class_type.is_model(value)
        choice = var.find_value_choice(value, check_subclass)
        func = self.write_value

        if not choice and check_subclass:
            func = self.write_xsi_type
            choice = var

    if not choice:
        raise SerializerError(
            f"XmlElements undefined choice: `{var.name}` for `{type(value)}`"
        )

    yield from func(value, choice, namespace)

write_element(value, var, namespace)

Convert a value assigned to an element field to sax events.

Parameters:

Name Type Description Default
value Any

A list instance of mixed type values

required
var XmlVar

The field metadata instance

required
namespace Optional[str]

The class namespace (unused)

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def write_element(
    self,
    value: Any,
    var: XmlVar,
    namespace: Optional[str],
) -> EventIterator:
    """Convert a value assigned to an element field to sax events.

    Args:
        value: A list instance of mixed type values
        var: The field metadata instance
        namespace: The class namespace (unused)

    Yields:
        An iterator of sax events.
    """
    yield XmlWriterEvent.START, var.qname

    if var.nillable:
        yield XmlWriterEvent.ATTR, QNames.XSI_NIL, "true"

    if value is not None and value != "" and var.any_type:
        datatype = DataType.from_value(value)
        if datatype != DataType.STRING:
            yield XmlWriterEvent.ATTR, QNames.XSI_TYPE, QName(str(datatype))

    yield XmlWriterEvent.DATA, self.encode(value, var)
    yield XmlWriterEvent.END, var.qname

write_data(value, var) classmethod

Convert a value assigned to a text field to sax events.

Parameters:

Name Type Description Default
value Any

A list instance of mixed type values

required
var XmlVar

The field metadata instance

required

Yields:

Type Description
EventIterator

An iterator of sax events.

Source code in xsdata/formats/dataclass/serializers/xml.py
477
478
479
480
481
482
483
484
485
486
487
488
@classmethod
def write_data(cls, value: Any, var: XmlVar) -> EventIterator:
    """Convert a value assigned to a text field to sax events.

    Args:
        value: A list instance of mixed type values
        var: The field metadata instance

    Yields:
        An iterator of sax events.
    """
    yield XmlWriterEvent.DATA, cls.encode(value, var)

next_value(obj, meta) classmethod

Produce the next non attribute value of a model instance to convert.

The generator will produce the values in the order the fields are defined in the model or by their sequence number.

Sequential fields need to be rendered together in parallel order eg:

Parameters:

Name Type Description Default
obj Any

The input model instance

required
meta XmlMeta

The model metadata instance

required

Yields:

Type Description
XmlVar

An iterator of field metadata instance and value tuples.

Source code in xsdata/formats/dataclass/serializers/xml.py
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
@classmethod
def next_value(cls, obj: Any, meta: XmlMeta) -> Iterator[Tuple[XmlVar, Any]]:
    """Produce the next non attribute value of a model instance to convert.

    The generator will produce the values in the order the fields
    are defined in the model or by their sequence number.

    Sequential fields need to be rendered together in parallel order
    eg: <a1/><a2/><a1/><a/2></a1>

    Args:
        obj: The input model instance
        meta: The model metadata instance

    Yields:
        An iterator of field metadata instance and value tuples.
    """
    index = 0
    attrs = meta.get_element_vars()
    stop = len(attrs)
    while index < stop:
        var = attrs[index]

        if var.sequence is None:
            value = getattr(obj, var.name)
            if value is not None or var.nillable:
                yield var, value
            index += 1
            continue

        indices = range(index, stop)
        end = next(
            i for i in indices[::-1] if attrs[i].sequence == var.sequence
        )  # pragma: no cover
        sequence = attrs[index : end + 1]
        index = end + 1
        j = 0

        rolling = True
        while rolling:
            rolling = False
            for var in sequence:
                values = getattr(obj, var.name)
                if collections.is_array(values):
                    if j < len(values):
                        rolling = True
                        value = values[j]
                        if value is not None or var.nillable:
                            yield var, value
                elif j == 0:
                    rolling = True
                    if values is not None or var.nillable:
                        yield var, values

            j += 1

next_attribute(obj, meta, nillable, xsi_type, ignore_optionals) classmethod

Produce the next attribute value to convert.

Parameters:

Name Type Description Default
obj Any

The input model instance

required
meta XmlMeta

The model metadata instance

required
nillable bool

Specifies if the current element supports nillable content

required
xsi_type Optional[str]

The real xsi:type of the object

required
ignore_optionals bool

Specifies if optional attributes with default values should be ignored.

required

Yields:

Type Description
str

An iterator of attribute name-value pairs.

Source code in xsdata/formats/dataclass/serializers/xml.py
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
@classmethod
def next_attribute(
    cls,
    obj: Any,
    meta: XmlMeta,
    nillable: bool,
    xsi_type: Optional[str],
    ignore_optionals: bool,
) -> Iterator[Tuple[str, Any]]:
    """Produce the next attribute value to convert.

    Args:
        obj: The input model instance
        meta: The model metadata instance
        nillable: Specifies if the current element supports nillable content
        xsi_type: The real xsi:type of the object
        ignore_optionals: Specifies if optional attributes with default
            values should be ignored.

    Yields:
        An iterator of attribute name-value pairs.
    """
    for var in meta.get_attribute_vars():
        if var.is_attribute:
            value = getattr(obj, var.name)
            if (
                value is None
                or (collections.is_array(value) and not value)
                or (ignore_optionals and var.is_optional(value))
            ):
                continue

            yield var.qname, cls.encode(value, var)
        else:
            yield from getattr(obj, var.name, EMPTY_MAP).items()

    if xsi_type:
        yield QNames.XSI_TYPE, QName(xsi_type)

    if nillable:
        yield QNames.XSI_NIL, "true"

encode(value, var) classmethod

Encode a value for xml serialization.

Converts values to strings. QName instances is an exception, those values need to wait until the XmlWriter assigns prefixes to namespaces per element node. Enums and Tokens may contain QName(s) so they also get a special treatment.

We can't do all the conversions in the writer because we would need to carry the xml vars inside the writer. Instead of that we do the easy encoding here and leave the qualified names for later.

Parameters:

Name Type Description Default
value Any

The simple type vale to encode

required
var XmlVar

The field metadata instance

required

Returns:

Type Description
Any

The encoded value.

Source code in xsdata/formats/dataclass/serializers/xml.py
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
@classmethod
def encode(cls, value: Any, var: XmlVar) -> Any:
    """Encode a value for xml serialization.

    Converts values to strings. QName instances is an exception,
    those values need to wait until the XmlWriter assigns prefixes
    to namespaces per element node. Enums and Tokens may contain
    QName(s) so they also get a special treatment.

    We can't do all the conversions in the writer because we would
    need to carry the xml vars inside the writer. Instead of that we
    do the easy encoding here and leave the qualified names for
    later.

    Args:
        value: The simple type vale to encode
        var: The field metadata instance

    Returns:
        The encoded value.
    """
    if isinstance(value, (str, QName)) or var is None:
        return value

    if collections.is_array(value):
        return [cls.encode(v, var) for v in value]

    if isinstance(value, Enum):
        return cls.encode(value.value, var)

    return converter.serialize(value, format=var.format)

real_xsi_type(qname, target_qname) classmethod

Compare the qname with the target qname and return the real xsi:type.

Parameters:

Name Type Description Default
qname str

The field type qualified name

required
target_qname Optional[str]

The value type qualified name

required

Returns:

Type Description
Optional[str]

None if the qname and target qname match, otherwise

Optional[str]

return the target qname.

Source code in xsdata/formats/dataclass/serializers/xml.py
620
621
622
623
624
625
626
627
628
629
630
631
632
@classmethod
def real_xsi_type(cls, qname: str, target_qname: Optional[str]) -> Optional[str]:
    """Compare the qname with the target qname and return the real xsi:type.

    Args:
        qname: The field type qualified name
        target_qname: The value type qualified name

    Returns:
        None if the qname and target qname match, otherwise
        return the target qname.
    """
    return target_qname if target_qname != qname else None