Skip to content

element

xsdata.formats.dataclass.parsers.nodes.element

ElementNode

Bases: XmlNode

XmlNode for complex elements.

Parameters:

Name Type Description Default
meta XmlMeta

The class binding metadata instance

required
attrs Dict

The element attributes

required
ns_map Dict

The element namespace prefix-URI map

required
config ParserConfig

The parser config instance

required
context XmlContext

The models context instance

required
position int

The current objects length, everything after this position are considered children of this node.

required
mixed bool

Specifies whether this node supports mixed content.

False
derived_factory Optional[Type]

Derived element factory

None
xsi_type Optional[str]

The xml type substitution

None
xsi_nil Optional[bool]

Specifies whether element has the xsi:nil attribute

None

Attributes:

Name Type Description
assigned Set[int]

A set to store the processed sub-nodes

tail_processed bool

Whether the tail process is consumed

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 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
633
634
635
class ElementNode(XmlNode):
    """XmlNode for complex elements.

    Args:
        meta: The class binding metadata instance
        attrs: The element attributes
        ns_map: The element namespace prefix-URI map
        config: The parser config instance
        context: The models context instance
        position: The current objects length, everything after
            this position are considered children of this node.
        mixed: Specifies whether this node supports mixed content.
        derived_factory: Derived element factory
        xsi_type: The xml type substitution
        xsi_nil: Specifies whether element has the xsi:nil attribute

    Attributes:
        assigned: A set to store the processed sub-nodes
        tail_processed: Whether the tail process is consumed
    """

    __slots__ = (
        "meta",
        "attrs",
        "ns_map",
        "config",
        "context",
        "position",
        "mixed",
        "derived_factory",
        "xsi_type",
        "xsi_nil",
        "assigned",
        "tail_processed",
    )

    def __init__(
        self,
        meta: XmlMeta,
        attrs: Dict,
        ns_map: Dict,
        config: ParserConfig,
        context: XmlContext,
        position: int,
        mixed: bool = False,
        derived_factory: Optional[Type] = None,
        xsi_type: Optional[str] = None,
        xsi_nil: Optional[bool] = None,
    ):
        self.meta = meta
        self.attrs = attrs
        self.ns_map = ns_map
        self.config = config
        self.context = context
        self.position = position
        self.mixed = mixed
        self.derived_factory = derived_factory
        self.xsi_type = xsi_type
        self.xsi_nil = xsi_nil
        self.assigned: Set[int] = set()
        self.tail_processed: bool = False

    def bind(
        self,
        qname: str,
        text: Optional[str],
        tail: Optional[str],
        objects: List[Any],
    ) -> bool:
        """Bind the parsed data into an object for the ending element.

        This entry point is called when a xml element ends and is
        responsible to parse the current element attributes/text, bind
        any children objects and initialize new object.

        Args:
            qname: The element qualified name
            text: The element text content
            tail: The element tail content
            objects: The list of intermediate parsed objects

        Returns:
            Whether the binding process was successful or not.
        """
        obj: Any = None
        if not self.xsi_nil or self.meta.nillable:
            params: Dict = {}
            self.bind_attrs(params)
            self.bind_content(params, text, tail, objects)
            obj = self.config.class_factory(self.meta.clazz, params)

        if self.derived_factory:
            obj = self.derived_factory(qname=qname, value=obj, type=self.xsi_type)

        objects.append((qname, obj))

        if self.mixed and not self.tail_processed:
            tail = ParserUtils.normalize_content(tail)
            if tail:
                objects.append((None, tail))

        return True

    def bind_content(
        self,
        params: Dict,
        text: Optional[str],
        tail: Optional[str],
        objects: List[Any],
    ):
        """Parse the text and tail content.

        Args:
            params: The class parameters
            text: The element text content
            tail: The element tail content
            objects: The list of intermediate parsed objects
        """
        wild_var = self.meta.find_any_wildcard()
        if wild_var and wild_var.mixed:
            self.bind_mixed_objects(params, wild_var, objects)
            bind_text = False
        else:
            self.bind_objects(params, objects)
            bind_text = self.bind_text(params, text)

        if not bind_text and wild_var:
            self.bind_wild_text(params, wild_var, text, tail)
            self.tail_processed = True

        for key in params:
            if isinstance(params[key], PendingCollection):
                params[key] = params[key].evaluate()

    def bind_attrs(self, params: Dict[str, Any]):
        """Parse the element attributes.

        Scenarios:
            - Each attribute matches a class field
            - Class has a wildcard field that sucks everything else

        Args:
            params: The class parameters

        Raises:
            ParserError: If the document contains an unknown attribute
                and the configuration is strict.
        """
        if not self.attrs:
            return

        for qname, value in self.attrs.items():
            var = self.meta.find_attribute(qname)
            if var and var.name not in params:
                self.bind_attr(params, var, value)
            else:
                var = self.meta.find_any_attributes(qname)
                if var:
                    self.bind_any_attr(params, var, qname, value)
                else:
                    if (
                        self.config.fail_on_unknown_attributes
                        and target_uri(qname) != Namespace.XSI.uri
                    ):
                        raise ParserError(
                            f"Unknown attribute {self.meta.qname}:{qname}"
                        )

    def bind_attr(self, params: Dict, var: XmlVar, value: Any):
        """Parse an element attribute.

        Ignores fields with init==false!

        Args:
            params: The class parameters
            var: The xml var instance
            value: The attribute value
        """
        value = ParserUtils.parse_var(
            meta=self.meta,
            var=var,
            config=self.config,
            value=value,
            ns_map=self.ns_map,
        )

        if var.init:
            params[var.name] = value
        else:
            ParserUtils.validate_fixed_value(self.meta, var, value)

    def bind_any_attr(self, params: Dict, var: XmlVar, qname: str, value: Any):
        """Parse an element attribute to a wildcard field.

        Args:
            params: The class parameters
            var: The xml var instance
            qname:  The attribute namespace qualified name
            value: The attribute value
        """
        if var.name not in params:
            params[var.name] = {}

        params[var.name][qname] = ParserUtils.parse_any_attribute(value, self.ns_map)

    def bind_objects(self, params: Dict, objects: List):
        """Bind children objects.

        Emit a warning if an object doesn't fit in any
        class parameters.

        Args:
            params: The class parameters
            objects: The list of intermediate parsed objects
        """
        position = self.position
        for qname, obj in objects[position:]:
            if not self.bind_object(params, qname, obj):
                logger.warning("Unassigned parsed object %s", qname)

        del objects[position:]

    def bind_object(self, params: Dict, qname: str, value: Any) -> bool:
        """Bind a child object.

        Args:
            params: The class parameters
            qname: The qualified name of the element
            value: The parsed object

        Returns:
            Whether the parsed object can fit in one of class
            parameters or not.
        """
        for var in self.meta.find_children(qname):
            if var.is_wildcard:
                return self.bind_wild_var(params, var, qname, value)

            if self.bind_var(params, var, value):
                return True

        return False

    @classmethod
    def bind_var(cls, params: Dict, var: XmlVar, value: Any) -> bool:
        """Bind a child object to an element field.

        Args:
            params: The class parameters
            var: The matched xml var instance
            value: The parsed object

        Returns:
            Whether the parsed object can fit in one of class
            parameters or not.
        """
        if var.init:
            if var.list_element:
                items = params.get(var.name)
                if items is None:
                    params[var.name] = PendingCollection([value], var.factory)
                else:
                    items.append(value)
            elif var.name not in params:
                params[var.name] = value
            else:
                return False

        return True

    def bind_wild_var(self, params: Dict, var: XmlVar, qname: str, value: Any) -> bool:
        """Bind a child object to a wildcard field.

        The wildcard might support one or more values. If it
        supports only one the values are nested under a parent
        generic element instance.

        Args:
            params: The class parameters
            var: The wildcard var instance
            qname: The qualified name of the element
            value: The parsed value

        Returns:
            Always true, since wildcard fields can absorb any value.
        """
        value = self.prepare_generic_value(qname, value)

        if var.list_element:
            items = params.get(var.name)
            if items is None:
                params[var.name] = PendingCollection([value], var.factory)
            else:
                items.append(value)
        elif var.name in params:
            previous = params[var.name]
            factory = self.context.class_type.any_element

            if not isinstance(previous, factory) or previous.qname:
                params[var.name] = factory(children=[previous])

            params[var.name].children.append(value)
        else:
            params[var.name] = value

        return True

    def bind_mixed_objects(self, params: Dict, var: XmlVar, objects: List):
        """Bind children objects to a mixed content wildcard field.

        Args:
            params: The class parameters
            var: The wildcard var instance
            objects: The list of intermediate parsed objects
        """
        pos = self.position
        params[var.name] = list(starmap(self.prepare_generic_value, objects[pos:]))
        del objects[pos:]

    def prepare_generic_value(self, qname: Optional[str], value: Any) -> Any:
        """Wrap primitive text nodes in a generic element.

        Args:
            qname: The qualified name of the element
            value: The parsed object

        Returns:
            The original parsed value if it's a data class, or
            the wrapped primitive value in a generic element.
        """
        if qname and not self.context.class_type.is_model(value):
            any_factory = self.context.class_type.any_element
            value = any_factory(qname=qname, text=converter.serialize(value))

        return value

    def bind_text(self, params: Dict, text: Optional[str]) -> bool:
        """Bind the element text content.

        Args:
            params: The class parameters
            text: The element text content

        Returns:
            Whether the text content can fit in one of class
            parameters or not.
        """
        var = self.meta.text

        if not var or (text is None and not self.xsi_nil):
            return False

        if self.xsi_nil and not text:
            value = None
        else:
            value = ParserUtils.parse_var(
                meta=self.meta,
                var=var,
                config=self.config,
                value=text,
                ns_map=self.ns_map,
            )

        if var.init:
            params[var.name] = value
        else:
            ParserUtils.validate_fixed_value(self.meta, var, value)

        return True

    def bind_wild_text(
        self,
        params: Dict,
        var: XmlVar,
        text: Optional[str],
        tail: Optional[str],
    ) -> bool:
        """Bind the element text and tail content to a wildcard field.

        If the field is a list, prepend the text and append the tail content.
        Otherwise, build a generic element with the text/tail content
        and any attributes. If the field is already occupied, then this
        means the current node is a child, and we need to nested them.

        Args:
            params: The class parameters
            var: The wildcard var instance
            text: The element text content
            tail: The element text content

        Returns:
            Whether the text content can fit in one of class
            parameters or not.
        """
        text = ParserUtils.normalize_content(text)
        tail = ParserUtils.normalize_content(tail)
        if text is tail is None:
            return False

        if var.list_element:
            items = params.get(var.name)
            if items is None:
                params[var.name] = items = PendingCollection(None, var.factory)

            items.insert(0, text)
            if tail:
                items.append(tail)

        else:
            previous = params.get(var.name, None)
            factory = self.context.class_type.any_element
            generic = factory(
                text=text,
                tail=tail,
                attributes=ParserUtils.parse_any_attributes(self.attrs, self.ns_map),
            )
            if previous:
                generic.children.append(previous)

            params[var.name] = generic

        return True

    def child(self, qname: str, attrs: Dict, ns_map: Dict, position: int) -> XmlNode:
        """Initialize the next child node to be queued, when an element starts.

        This entry point is responsible to create the next node type
        with all the necessary information on how to bind the incoming
        input data.

        Args:
            qname: The element qualified name
            attrs: The element attributes
            ns_map: The element namespace prefix-URI map
            position: The current length of the intermediate objects

        Raises:
            ParserError: If the child element is unknown
        """
        for var in self.meta.find_children(qname):
            unique = 0 if not var.is_element or var.list_element else var.index
            if not unique or unique not in self.assigned:
                node = self.build_node(qname, var, attrs, ns_map, position)

                if node:
                    if unique:
                        self.assigned.add(unique)

                    return node

        if self.config.fail_on_unknown_properties:
            raise ParserError(f"Unknown property {self.meta.qname}:{qname}")

        return nodes.SkipNode()

    def build_node(
        self,
        qname: str,
        var: XmlVar,
        attrs: Dict,
        ns_map: Dict,
        position: int,
    ) -> Optional[XmlNode]:
        """Build the next child node based on the xml var instance.

        Args:
            qname: The element qualified name
            var: The xml var instance
            attrs: The element attributes
            ns_map: The element namespace prefix-URI map
            position: The current length of the intermediate objects

        Returns:
            The next child node instance, or None if nothing matched
            the starting element.
        """
        if var.is_clazz_union:
            return nodes.UnionNode(
                meta=self.meta,
                var=var,
                attrs=attrs,
                ns_map=ns_map,
                config=self.config,
                context=self.context,
                position=position,
            )

        xsi_type = ParserUtils.xsi_type(attrs, ns_map)
        xsi_nil = ParserUtils.xsi_nil(attrs)
        derived_factory = self.context.class_type.derived_element

        if var.clazz:
            return self.build_element_node(
                var.clazz,
                False,
                var.nillable,
                attrs,
                ns_map,
                position,
                derived_factory,
                xsi_type,
                xsi_nil,
            )

        if not var.any_type and not var.is_wildcard:
            return nodes.PrimitiveNode(self.meta, var, ns_map, self.config)

        datatype = DataType.from_qname(xsi_type) if xsi_type else None
        derived = var.is_wildcard
        if datatype:
            return nodes.StandardNode(
                self.meta,
                var,
                datatype,
                ns_map,
                self.config,
                var.nillable,
                derived_factory if derived else None,
            )

        node = None
        clazz = None
        if xsi_type:
            clazz = self.context.find_type(xsi_type)

        if clazz:
            node = self.build_element_node(
                clazz,
                derived,
                var.nillable,
                attrs,
                ns_map,
                position,
                derived_factory,
                xsi_type,
                xsi_nil,
            )

        if node:
            return node

        if var.process_contents != "skip":
            clazz = self.context.find_type(qname)

        if clazz:
            node = self.build_element_node(
                clazz,
                False,
                var.nillable,
                attrs,
                ns_map,
                position,
                None,
                xsi_type,
                xsi_nil,
            )

        if node:
            return node

        return nodes.WildcardNode(
            var=var,
            attrs=attrs,
            ns_map=ns_map,
            position=position,
            factory=self.context.class_type.any_element,
        )

    def build_element_node(
        self,
        clazz: Type,
        derived: bool,
        nillable: bool,
        attrs: Dict,
        ns_map: Dict,
        position: int,
        derived_factory: Type,
        xsi_type: Optional[str] = None,
        xsi_nil: Optional[bool] = None,
    ) -> Optional["ElementNode"]:
        """Build the next element child node.

        Args:
            clazz: The target class
            derived: Whether derived elements should wrap the parsed object
            nillable: Specifies whether nil content is allowed
            attrs: The element attributes
            ns_map: The element namespace prefix-URI map
            position: The current length of the intermediate objects
            derived_factory: The derived factory
            xsi_type: The xml type substitution
            xsi_nil: Specifies whether the node supports nillable content

        Returns:
            The next child element node instance, or None if the
            clazz doesn't match the starting element.
        """
        meta = self.context.fetch(clazz, self.meta.namespace, xsi_type)
        nillable = nillable or meta.nillable

        if not meta or (xsi_nil is not None and nillable != xsi_nil):
            return None

        if xsi_type and not derived and not issubclass(meta.clazz, clazz):
            derived = True

        return ElementNode(
            meta=meta,
            config=self.config,
            attrs=attrs,
            ns_map=ns_map,
            context=self.context,
            position=position,
            derived_factory=derived_factory if derived else None,
            xsi_type=xsi_type,
            xsi_nil=xsi_nil,
            mixed=self.meta.mixed_content,
        )

bind(qname, text, tail, objects)

Bind the parsed data into an object for the ending element.

This entry point is called when a xml element ends and is responsible to parse the current element attributes/text, bind any children objects and initialize new object.

Parameters:

Name Type Description Default
qname str

The element qualified name

required
text Optional[str]

The element text content

required
tail Optional[str]

The element tail content

required
objects List[Any]

The list of intermediate parsed objects

required

Returns:

Type Description
bool

Whether the binding process was successful or not.

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
 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
def bind(
    self,
    qname: str,
    text: Optional[str],
    tail: Optional[str],
    objects: List[Any],
) -> bool:
    """Bind the parsed data into an object for the ending element.

    This entry point is called when a xml element ends and is
    responsible to parse the current element attributes/text, bind
    any children objects and initialize new object.

    Args:
        qname: The element qualified name
        text: The element text content
        tail: The element tail content
        objects: The list of intermediate parsed objects

    Returns:
        Whether the binding process was successful or not.
    """
    obj: Any = None
    if not self.xsi_nil or self.meta.nillable:
        params: Dict = {}
        self.bind_attrs(params)
        self.bind_content(params, text, tail, objects)
        obj = self.config.class_factory(self.meta.clazz, params)

    if self.derived_factory:
        obj = self.derived_factory(qname=qname, value=obj, type=self.xsi_type)

    objects.append((qname, obj))

    if self.mixed and not self.tail_processed:
        tail = ParserUtils.normalize_content(tail)
        if tail:
            objects.append((None, tail))

    return True

bind_content(params, text, tail, objects)

Parse the text and tail content.

Parameters:

Name Type Description Default
params Dict

The class parameters

required
text Optional[str]

The element text content

required
tail Optional[str]

The element tail content

required
objects List[Any]

The list of intermediate parsed objects

required
Source code in xsdata/formats/dataclass/parsers/nodes/element.py
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
def bind_content(
    self,
    params: Dict,
    text: Optional[str],
    tail: Optional[str],
    objects: List[Any],
):
    """Parse the text and tail content.

    Args:
        params: The class parameters
        text: The element text content
        tail: The element tail content
        objects: The list of intermediate parsed objects
    """
    wild_var = self.meta.find_any_wildcard()
    if wild_var and wild_var.mixed:
        self.bind_mixed_objects(params, wild_var, objects)
        bind_text = False
    else:
        self.bind_objects(params, objects)
        bind_text = self.bind_text(params, text)

    if not bind_text and wild_var:
        self.bind_wild_text(params, wild_var, text, tail)
        self.tail_processed = True

    for key in params:
        if isinstance(params[key], PendingCollection):
            params[key] = params[key].evaluate()

bind_attrs(params)

Parse the element attributes.

Scenarios
  • Each attribute matches a class field
  • Class has a wildcard field that sucks everything else

Parameters:

Name Type Description Default
params Dict[str, Any]

The class parameters

required

Raises:

Type Description
ParserError

If the document contains an unknown attribute and the configuration is strict.

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
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
def bind_attrs(self, params: Dict[str, Any]):
    """Parse the element attributes.

    Scenarios:
        - Each attribute matches a class field
        - Class has a wildcard field that sucks everything else

    Args:
        params: The class parameters

    Raises:
        ParserError: If the document contains an unknown attribute
            and the configuration is strict.
    """
    if not self.attrs:
        return

    for qname, value in self.attrs.items():
        var = self.meta.find_attribute(qname)
        if var and var.name not in params:
            self.bind_attr(params, var, value)
        else:
            var = self.meta.find_any_attributes(qname)
            if var:
                self.bind_any_attr(params, var, qname, value)
            else:
                if (
                    self.config.fail_on_unknown_attributes
                    and target_uri(qname) != Namespace.XSI.uri
                ):
                    raise ParserError(
                        f"Unknown attribute {self.meta.qname}:{qname}"
                    )

bind_attr(params, var, value)

Parse an element attribute.

Ignores fields with init==false!

Parameters:

Name Type Description Default
params Dict

The class parameters

required
var XmlVar

The xml var instance

required
value Any

The attribute value

required
Source code in xsdata/formats/dataclass/parsers/nodes/element.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def bind_attr(self, params: Dict, var: XmlVar, value: Any):
    """Parse an element attribute.

    Ignores fields with init==false!

    Args:
        params: The class parameters
        var: The xml var instance
        value: The attribute value
    """
    value = ParserUtils.parse_var(
        meta=self.meta,
        var=var,
        config=self.config,
        value=value,
        ns_map=self.ns_map,
    )

    if var.init:
        params[var.name] = value
    else:
        ParserUtils.validate_fixed_value(self.meta, var, value)

bind_any_attr(params, var, qname, value)

Parse an element attribute to a wildcard field.

Parameters:

Name Type Description Default
params Dict

The class parameters

required
var XmlVar

The xml var instance

required
qname str

The attribute namespace qualified name

required
value Any

The attribute value

required
Source code in xsdata/formats/dataclass/parsers/nodes/element.py
209
210
211
212
213
214
215
216
217
218
219
220
221
def bind_any_attr(self, params: Dict, var: XmlVar, qname: str, value: Any):
    """Parse an element attribute to a wildcard field.

    Args:
        params: The class parameters
        var: The xml var instance
        qname:  The attribute namespace qualified name
        value: The attribute value
    """
    if var.name not in params:
        params[var.name] = {}

    params[var.name][qname] = ParserUtils.parse_any_attribute(value, self.ns_map)

bind_objects(params, objects)

Bind children objects.

Emit a warning if an object doesn't fit in any class parameters.

Parameters:

Name Type Description Default
params Dict

The class parameters

required
objects List

The list of intermediate parsed objects

required
Source code in xsdata/formats/dataclass/parsers/nodes/element.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
def bind_objects(self, params: Dict, objects: List):
    """Bind children objects.

    Emit a warning if an object doesn't fit in any
    class parameters.

    Args:
        params: The class parameters
        objects: The list of intermediate parsed objects
    """
    position = self.position
    for qname, obj in objects[position:]:
        if not self.bind_object(params, qname, obj):
            logger.warning("Unassigned parsed object %s", qname)

    del objects[position:]

bind_object(params, qname, value)

Bind a child object.

Parameters:

Name Type Description Default
params Dict

The class parameters

required
qname str

The qualified name of the element

required
value Any

The parsed object

required

Returns:

Type Description
bool

Whether the parsed object can fit in one of class

bool

parameters or not.

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
def bind_object(self, params: Dict, qname: str, value: Any) -> bool:
    """Bind a child object.

    Args:
        params: The class parameters
        qname: The qualified name of the element
        value: The parsed object

    Returns:
        Whether the parsed object can fit in one of class
        parameters or not.
    """
    for var in self.meta.find_children(qname):
        if var.is_wildcard:
            return self.bind_wild_var(params, var, qname, value)

        if self.bind_var(params, var, value):
            return True

    return False

bind_var(params, var, value) classmethod

Bind a child object to an element field.

Parameters:

Name Type Description Default
params Dict

The class parameters

required
var XmlVar

The matched xml var instance

required
value Any

The parsed object

required

Returns:

Type Description
bool

Whether the parsed object can fit in one of class

bool

parameters or not.

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
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
@classmethod
def bind_var(cls, params: Dict, var: XmlVar, value: Any) -> bool:
    """Bind a child object to an element field.

    Args:
        params: The class parameters
        var: The matched xml var instance
        value: The parsed object

    Returns:
        Whether the parsed object can fit in one of class
        parameters or not.
    """
    if var.init:
        if var.list_element:
            items = params.get(var.name)
            if items is None:
                params[var.name] = PendingCollection([value], var.factory)
            else:
                items.append(value)
        elif var.name not in params:
            params[var.name] = value
        else:
            return False

    return True

bind_wild_var(params, var, qname, value)

Bind a child object to a wildcard field.

The wildcard might support one or more values. If it supports only one the values are nested under a parent generic element instance.

Parameters:

Name Type Description Default
params Dict

The class parameters

required
var XmlVar

The wildcard var instance

required
qname str

The qualified name of the element

required
value Any

The parsed value

required

Returns:

Type Description
bool

Always true, since wildcard fields can absorb any value.

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
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
def bind_wild_var(self, params: Dict, var: XmlVar, qname: str, value: Any) -> bool:
    """Bind a child object to a wildcard field.

    The wildcard might support one or more values. If it
    supports only one the values are nested under a parent
    generic element instance.

    Args:
        params: The class parameters
        var: The wildcard var instance
        qname: The qualified name of the element
        value: The parsed value

    Returns:
        Always true, since wildcard fields can absorb any value.
    """
    value = self.prepare_generic_value(qname, value)

    if var.list_element:
        items = params.get(var.name)
        if items is None:
            params[var.name] = PendingCollection([value], var.factory)
        else:
            items.append(value)
    elif var.name in params:
        previous = params[var.name]
        factory = self.context.class_type.any_element

        if not isinstance(previous, factory) or previous.qname:
            params[var.name] = factory(children=[previous])

        params[var.name].children.append(value)
    else:
        params[var.name] = value

    return True

bind_mixed_objects(params, var, objects)

Bind children objects to a mixed content wildcard field.

Parameters:

Name Type Description Default
params Dict

The class parameters

required
var XmlVar

The wildcard var instance

required
objects List

The list of intermediate parsed objects

required
Source code in xsdata/formats/dataclass/parsers/nodes/element.py
325
326
327
328
329
330
331
332
333
334
335
def bind_mixed_objects(self, params: Dict, var: XmlVar, objects: List):
    """Bind children objects to a mixed content wildcard field.

    Args:
        params: The class parameters
        var: The wildcard var instance
        objects: The list of intermediate parsed objects
    """
    pos = self.position
    params[var.name] = list(starmap(self.prepare_generic_value, objects[pos:]))
    del objects[pos:]

prepare_generic_value(qname, value)

Wrap primitive text nodes in a generic element.

Parameters:

Name Type Description Default
qname Optional[str]

The qualified name of the element

required
value Any

The parsed object

required

Returns:

Type Description
Any

The original parsed value if it's a data class, or

Any

the wrapped primitive value in a generic element.

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
def prepare_generic_value(self, qname: Optional[str], value: Any) -> Any:
    """Wrap primitive text nodes in a generic element.

    Args:
        qname: The qualified name of the element
        value: The parsed object

    Returns:
        The original parsed value if it's a data class, or
        the wrapped primitive value in a generic element.
    """
    if qname and not self.context.class_type.is_model(value):
        any_factory = self.context.class_type.any_element
        value = any_factory(qname=qname, text=converter.serialize(value))

    return value

bind_text(params, text)

Bind the element text content.

Parameters:

Name Type Description Default
params Dict

The class parameters

required
text Optional[str]

The element text content

required

Returns:

Type Description
bool

Whether the text content can fit in one of class

bool

parameters or not.

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
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
def bind_text(self, params: Dict, text: Optional[str]) -> bool:
    """Bind the element text content.

    Args:
        params: The class parameters
        text: The element text content

    Returns:
        Whether the text content can fit in one of class
        parameters or not.
    """
    var = self.meta.text

    if not var or (text is None and not self.xsi_nil):
        return False

    if self.xsi_nil and not text:
        value = None
    else:
        value = ParserUtils.parse_var(
            meta=self.meta,
            var=var,
            config=self.config,
            value=text,
            ns_map=self.ns_map,
        )

    if var.init:
        params[var.name] = value
    else:
        ParserUtils.validate_fixed_value(self.meta, var, value)

    return True

bind_wild_text(params, var, text, tail)

Bind the element text and tail content to a wildcard field.

If the field is a list, prepend the text and append the tail content. Otherwise, build a generic element with the text/tail content and any attributes. If the field is already occupied, then this means the current node is a child, and we need to nested them.

Parameters:

Name Type Description Default
params Dict

The class parameters

required
var XmlVar

The wildcard var instance

required
text Optional[str]

The element text content

required
tail Optional[str]

The element text content

required

Returns:

Type Description
bool

Whether the text content can fit in one of class

bool

parameters or not.

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
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
def bind_wild_text(
    self,
    params: Dict,
    var: XmlVar,
    text: Optional[str],
    tail: Optional[str],
) -> bool:
    """Bind the element text and tail content to a wildcard field.

    If the field is a list, prepend the text and append the tail content.
    Otherwise, build a generic element with the text/tail content
    and any attributes. If the field is already occupied, then this
    means the current node is a child, and we need to nested them.

    Args:
        params: The class parameters
        var: The wildcard var instance
        text: The element text content
        tail: The element text content

    Returns:
        Whether the text content can fit in one of class
        parameters or not.
    """
    text = ParserUtils.normalize_content(text)
    tail = ParserUtils.normalize_content(tail)
    if text is tail is None:
        return False

    if var.list_element:
        items = params.get(var.name)
        if items is None:
            params[var.name] = items = PendingCollection(None, var.factory)

        items.insert(0, text)
        if tail:
            items.append(tail)

    else:
        previous = params.get(var.name, None)
        factory = self.context.class_type.any_element
        generic = factory(
            text=text,
            tail=tail,
            attributes=ParserUtils.parse_any_attributes(self.attrs, self.ns_map),
        )
        if previous:
            generic.children.append(previous)

        params[var.name] = generic

    return True

child(qname, attrs, ns_map, position)

Initialize the next child node to be queued, when an element starts.

This entry point is responsible to create the next node type with all the necessary information on how to bind the incoming input data.

Parameters:

Name Type Description Default
qname str

The element qualified name

required
attrs Dict

The element attributes

required
ns_map Dict

The element namespace prefix-URI map

required
position int

The current length of the intermediate objects

required

Raises:

Type Description
ParserError

If the child element is unknown

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
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
def child(self, qname: str, attrs: Dict, ns_map: Dict, position: int) -> XmlNode:
    """Initialize the next child node to be queued, when an element starts.

    This entry point is responsible to create the next node type
    with all the necessary information on how to bind the incoming
    input data.

    Args:
        qname: The element qualified name
        attrs: The element attributes
        ns_map: The element namespace prefix-URI map
        position: The current length of the intermediate objects

    Raises:
        ParserError: If the child element is unknown
    """
    for var in self.meta.find_children(qname):
        unique = 0 if not var.is_element or var.list_element else var.index
        if not unique or unique not in self.assigned:
            node = self.build_node(qname, var, attrs, ns_map, position)

            if node:
                if unique:
                    self.assigned.add(unique)

                return node

    if self.config.fail_on_unknown_properties:
        raise ParserError(f"Unknown property {self.meta.qname}:{qname}")

    return nodes.SkipNode()

build_node(qname, var, attrs, ns_map, position)

Build the next child node based on the xml var instance.

Parameters:

Name Type Description Default
qname str

The element qualified name

required
var XmlVar

The xml var instance

required
attrs Dict

The element attributes

required
ns_map Dict

The element namespace prefix-URI map

required
position int

The current length of the intermediate objects

required

Returns:

Type Description
Optional[XmlNode]

The next child node instance, or None if nothing matched

Optional[XmlNode]

the starting element.

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
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
def build_node(
    self,
    qname: str,
    var: XmlVar,
    attrs: Dict,
    ns_map: Dict,
    position: int,
) -> Optional[XmlNode]:
    """Build the next child node based on the xml var instance.

    Args:
        qname: The element qualified name
        var: The xml var instance
        attrs: The element attributes
        ns_map: The element namespace prefix-URI map
        position: The current length of the intermediate objects

    Returns:
        The next child node instance, or None if nothing matched
        the starting element.
    """
    if var.is_clazz_union:
        return nodes.UnionNode(
            meta=self.meta,
            var=var,
            attrs=attrs,
            ns_map=ns_map,
            config=self.config,
            context=self.context,
            position=position,
        )

    xsi_type = ParserUtils.xsi_type(attrs, ns_map)
    xsi_nil = ParserUtils.xsi_nil(attrs)
    derived_factory = self.context.class_type.derived_element

    if var.clazz:
        return self.build_element_node(
            var.clazz,
            False,
            var.nillable,
            attrs,
            ns_map,
            position,
            derived_factory,
            xsi_type,
            xsi_nil,
        )

    if not var.any_type and not var.is_wildcard:
        return nodes.PrimitiveNode(self.meta, var, ns_map, self.config)

    datatype = DataType.from_qname(xsi_type) if xsi_type else None
    derived = var.is_wildcard
    if datatype:
        return nodes.StandardNode(
            self.meta,
            var,
            datatype,
            ns_map,
            self.config,
            var.nillable,
            derived_factory if derived else None,
        )

    node = None
    clazz = None
    if xsi_type:
        clazz = self.context.find_type(xsi_type)

    if clazz:
        node = self.build_element_node(
            clazz,
            derived,
            var.nillable,
            attrs,
            ns_map,
            position,
            derived_factory,
            xsi_type,
            xsi_nil,
        )

    if node:
        return node

    if var.process_contents != "skip":
        clazz = self.context.find_type(qname)

    if clazz:
        node = self.build_element_node(
            clazz,
            False,
            var.nillable,
            attrs,
            ns_map,
            position,
            None,
            xsi_type,
            xsi_nil,
        )

    if node:
        return node

    return nodes.WildcardNode(
        var=var,
        attrs=attrs,
        ns_map=ns_map,
        position=position,
        factory=self.context.class_type.any_element,
    )

build_element_node(clazz, derived, nillable, attrs, ns_map, position, derived_factory, xsi_type=None, xsi_nil=None)

Build the next element child node.

Parameters:

Name Type Description Default
clazz Type

The target class

required
derived bool

Whether derived elements should wrap the parsed object

required
nillable bool

Specifies whether nil content is allowed

required
attrs Dict

The element attributes

required
ns_map Dict

The element namespace prefix-URI map

required
position int

The current length of the intermediate objects

required
derived_factory Type

The derived factory

required
xsi_type Optional[str]

The xml type substitution

None
xsi_nil Optional[bool]

Specifies whether the node supports nillable content

None

Returns:

Type Description
Optional[ElementNode]

The next child element node instance, or None if the

Optional[ElementNode]

clazz doesn't match the starting element.

Source code in xsdata/formats/dataclass/parsers/nodes/element.py
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
633
634
635
def build_element_node(
    self,
    clazz: Type,
    derived: bool,
    nillable: bool,
    attrs: Dict,
    ns_map: Dict,
    position: int,
    derived_factory: Type,
    xsi_type: Optional[str] = None,
    xsi_nil: Optional[bool] = None,
) -> Optional["ElementNode"]:
    """Build the next element child node.

    Args:
        clazz: The target class
        derived: Whether derived elements should wrap the parsed object
        nillable: Specifies whether nil content is allowed
        attrs: The element attributes
        ns_map: The element namespace prefix-URI map
        position: The current length of the intermediate objects
        derived_factory: The derived factory
        xsi_type: The xml type substitution
        xsi_nil: Specifies whether the node supports nillable content

    Returns:
        The next child element node instance, or None if the
        clazz doesn't match the starting element.
    """
    meta = self.context.fetch(clazz, self.meta.namespace, xsi_type)
    nillable = nillable or meta.nillable

    if not meta or (xsi_nil is not None and nillable != xsi_nil):
        return None

    if xsi_type and not derived and not issubclass(meta.clazz, clazz):
        derived = True

    return ElementNode(
        meta=meta,
        config=self.config,
        attrs=attrs,
        ns_map=ns_map,
        context=self.context,
        position=position,
        derived_factory=derived_factory if derived else None,
        xsi_type=xsi_type,
        xsi_nil=xsi_nil,
        mixed=self.meta.mixed_content,
    )