Skip to content

# builders

## `xsdata.formats.dataclass.models.builders`

### `ClassMeta`

The binding model combined metadata.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`element_name_generator` | `Callable` | The element name generator | required
`attribute_name_generator` | `Callable` | The attribute name generator | required
`qname` | `str` | The namespace qualified name of the class | required
`local_name` | `str` | The name of the element this class represents | required
`nillable` | `bool` | Specifies whether this class supports nillable content | required
`namespace` | `str | None` | The class namespace | required
`target_qname` | `str | None` | The class target namespace qualified name | required

Source code in `xsdata/formats/dataclass/models/builders.py`

    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

    class ClassMeta:
        """The binding model combined metadata.

        Args:
            element_name_generator: The element name generator
            attribute_name_generator: The attribute name generator
            qname: The namespace qualified name of the class
            local_name: The name of the element this class represents
            nillable: Specifies whether this class supports nillable content
            namespace: The class namespace
            target_qname: The class target namespace qualified name
        """

        __slots__ = (
            "attribute_name_generator",
            "element_name_generator",
            "local_name",
            "namespace",
            "nillable",
            "qname",
            "target_qname",
        )

        def __init__(
            self,
            element_name_generator: Callable,
            attribute_name_generator: Callable,
            qname: str,
            local_name: str,
            nillable: bool,
            namespace: str | None,
            target_qname: str | None,
        ):
            """Initialize class meta."""
            self.element_name_generator = element_name_generator
            self.attribute_name_generator = attribute_name_generator
            self.qname = qname
            self.local_name = local_name
            self.nillable = nillable
            self.namespace = namespace
            self.target_qname = target_qname

#### `__init__(element_name_generator, attribute_name_generator, qname, local_name, nillable, namespace, target_qname)`

Initialize class meta.

Source code in `xsdata/formats/dataclass/models/builders.py`

    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79

    def __init__(
        self,
        element_name_generator: Callable,
        attribute_name_generator: Callable,
        qname: str,
        local_name: str,
        nillable: bool,
        namespace: str | None,
        target_qname: str | None,
    ):
        """Initialize class meta."""
        self.element_name_generator = element_name_generator
        self.attribute_name_generator = attribute_name_generator
        self.qname = qname
        self.local_name = local_name
        self.nillable = nillable
        self.namespace = namespace
        self.target_qname = target_qname

### `XmlMetaBuilder`

Binding class metadata builder.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`class_type` | `ClassType` | The supported class type, e.g. dataclass, attr, pydantic | required
`element_name_generator` | `Callable` | The default element name generator | required
`attribute_name_generator` | `Callable` | The default attribute name generator | required
`globalns` | `dict[str, Callable] | None` | The global namespace | `None`

Source code in `xsdata/formats/dataclass/models/builders.py`

     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

    class XmlMetaBuilder:
        """Binding class metadata builder.

        Args:
            class_type: The supported class type, e.g. dataclass, attr, pydantic
            element_name_generator: The default element name generator
            attribute_name_generator: The default attribute name generator
            globalns: The global namespace
        """

        __slots__ = (
            "attribute_name_generator",
            "class_type",
            "element_name_generator",
            "globalns",
        )

        def __init__(
            self,
            class_type: ClassType,
            element_name_generator: Callable,
            attribute_name_generator: Callable,
            globalns: dict[str, Callable] | None = None,
        ):
            """Initialize the builder."""
            self.class_type = class_type
            self.element_name_generator = element_name_generator
            self.attribute_name_generator = attribute_name_generator
            self.globalns = globalns

        def build(self, clazz: type, parent_namespace: str | None) -> XmlMeta:
            """Build the binding metadata for a dataclass and its fields.

            Args:
                clazz: The target class
                parent_namespace: The parent class namespace

            Returns:
                The binding metadata instance.
            """
            self.class_type.verify_model(clazz)

            meta = self.build_class_meta(clazz, parent_namespace)
            class_vars = self.build_vars(
                clazz,
                meta.namespace,
                meta.element_name_generator,
                meta.attribute_name_generator,
            )

            attributes = {}
            elements: dict[str, list[XmlVar]] = defaultdict(list)
            wrappers: dict[str, str] = {}
            choices = []
            any_attributes = []
            wildcards = []
            text = None

            for var in class_vars:
                if var.is_attribute:
                    attributes[var.qname] = var
                elif var.is_element:
                    elements[var.qname].append(var)
                    if var.wrapper_qname:
                        wrappers[var.wrapper_qname] = var.qname
                elif var.is_elements:
                    choices.append(var)
                elif var.is_attributes:
                    any_attributes.append(var)
                elif var.is_wildcard:
                    wildcards.append(var)
                else:  # var.is_text
                    text = var

            return XmlMeta(
                clazz=clazz,
                qname=meta.qname,
                target_qname=meta.target_qname,
                nillable=meta.nillable,
                text=text,
                attributes=attributes,
                elements=elements,
                choices=choices,
                any_attributes=any_attributes,
                wildcards=wildcards,
                wrappers=wrappers,
            )

        def build_vars(
            self,
            clazz: type,
            namespace: str | None,
            element_name_generator: Callable,
            attribute_name_generator: Callable,
        ) -> Iterator[XmlVar]:
            """Build the binding metadata for the given dataclass fields.

            Args:
                clazz: The target class
                namespace: The target class namespace
                element_name_generator: The class element name generator
                attribute_name_generator: The class attribute name generator

            Yields:
                An iterator of the field binding metadata instances.
            """
            type_hints = get_type_hints(clazz, globalns=self.globalns)
            builder = XmlVarBuilder(
                class_type=self.class_type,
                default_xml_type=self.default_xml_type(clazz),
                element_name_generator=element_name_generator,
                attribute_name_generator=attribute_name_generator,
            )

            for field in self.class_type.get_fields(clazz):
                real_clazz = self.find_declared_class(clazz, field.name)
                globalns = sys.modules[real_clazz.__module__].__dict__
                parent_namespace = namespace
                if real_clazz is not clazz and "Meta" in real_clazz.__dict__:
                    parent_namespace = getattr(real_clazz.Meta, "namespace", namespace)

                var = builder.build(
                    clazz,
                    field.name,
                    type_hints[field.name],
                    field.metadata,
                    field.init,
                    parent_namespace,
                    self.class_type.default_value(field),
                    globalns,
                )
                if var is not None:
                    yield var

        def build_class_meta(
            self,
            clazz: Any,
            parent_namespace: str | None = None,
        ) -> ClassMeta:
            """Build the class meta options and merge with the defaults.

            The class metaclass is not inheritable.

            Args:
                clazz: The target class
                parent_namespace: The parent class namespace

            Returns:
                A class meta instance.
            """
            meta = clazz.Meta if "Meta" in clazz.__dict__ else None
            element_name_generator = getattr(
                meta, "element_name_generator", self.element_name_generator
            )
            attribute_name_generator = getattr(
                meta, "attribute_name_generator", self.attribute_name_generator
            )
            global_type = getattr(meta, "global_type", True)
            local_name = getattr(meta, "name", None)
            local_name = local_name or element_name_generator(clazz.__name__)
            nillable = getattr(meta, "nillable", False)
            namespace = getattr(meta, "namespace", parent_namespace)
            qname = build_qname(namespace, local_name)

            if self.is_inner_class(clazz) or not global_type:
                target_qname = None
            else:
                module = sys.modules[clazz.__module__]
                target_namespace = self.target_namespace(module, meta)
                target_qname = build_qname(target_namespace, local_name)

            return ClassMeta(
                element_name_generator,
                attribute_name_generator,
                qname,
                local_name,
                nillable,
                namespace,
                target_qname,
            )

        @classmethod
        def find_declared_class(cls, clazz: type, name: str) -> Any:
            """Find the user class that matches the name.

            Todo: Honestly I have no idea why we needed this.
            """
            for base in clazz.__mro__:
                ann = inspect.get_annotations(base)

                if ann and name in ann:
                    return base

            raise XmlContextError(f"Failed to detect the declared class for field {name}")

        @classmethod
        def is_inner_class(cls, clazz: type) -> bool:
            """Return whether the given type is nested inside another type."""
            return "." in clazz.__qualname__

        @classmethod
        def target_namespace(cls, module: Any, meta: Any) -> str | None:
            """The target namespace this class metadata was defined in."""
            namespace = getattr(meta, "target_namespace", None)
            if namespace is not None:
                return namespace

            namespace = getattr(module, "__NAMESPACE__", None)
            if namespace is not None:
                return namespace

            return getattr(meta, "namespace", None)

        def default_xml_type(self, clazz: type) -> str:
            """Return the default xml type for the fields of the given dataclass.

            If a class has fields with no xml type defined, attempt
            to figure it from the rest of the fields. It's either
            a text or an element field.

            # Todo hacks like this are so unnecessary...
            """
            counters: dict[str, int] = defaultdict(int)
            for var in self.class_type.get_fields(clazz):
                xml_type = var.metadata.get("type")
                counters[xml_type or "undefined"] += 1

            if counters[XmlType.TEXT] > 1:
                raise XmlContextError(
                    f"Dataclass `{clazz.__name__}` includes more than one text node!"
                )

            if counters["undefined"] == 1 and counters[XmlType.TEXT] == 0:
                return XmlType.TEXT

            return XmlType.ELEMENT

#### `__init__(class_type, element_name_generator, attribute_name_generator, globalns=None)`

Initialize the builder.

Source code in `xsdata/formats/dataclass/models/builders.py`

     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110

    def __init__(
        self,
        class_type: ClassType,
        element_name_generator: Callable,
        attribute_name_generator: Callable,
        globalns: dict[str, Callable] | None = None,
    ):
        """Initialize the builder."""
        self.class_type = class_type
        self.element_name_generator = element_name_generator
        self.attribute_name_generator = attribute_name_generator
        self.globalns = globalns

#### `build(clazz, parent_namespace)`

Build the binding metadata for a dataclass and its fields.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`clazz` | `type` | The target class | required
`parent_namespace` | `str | None` | The parent class namespace | required

Returns:

Type | Description
--- | ---
`XmlMeta` | The binding metadata instance.

Source code in `xsdata/formats/dataclass/models/builders.py`

    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

    def build(self, clazz: type, parent_namespace: str | None) -> XmlMeta:
        """Build the binding metadata for a dataclass and its fields.

        Args:
            clazz: The target class
            parent_namespace: The parent class namespace

        Returns:
            The binding metadata instance.
        """
        self.class_type.verify_model(clazz)

        meta = self.build_class_meta(clazz, parent_namespace)
        class_vars = self.build_vars(
            clazz,
            meta.namespace,
            meta.element_name_generator,
            meta.attribute_name_generator,
        )

        attributes = {}
        elements: dict[str, list[XmlVar]] = defaultdict(list)
        wrappers: dict[str, str] = {}
        choices = []
        any_attributes = []
        wildcards = []
        text = None

        for var in class_vars:
            if var.is_attribute:
                attributes[var.qname] = var
            elif var.is_element:
                elements[var.qname].append(var)
                if var.wrapper_qname:
                    wrappers[var.wrapper_qname] = var.qname
            elif var.is_elements:
                choices.append(var)
            elif var.is_attributes:
                any_attributes.append(var)
            elif var.is_wildcard:
                wildcards.append(var)
            else:  # var.is_text
                text = var

        return XmlMeta(
            clazz=clazz,
            qname=meta.qname,
            target_qname=meta.target_qname,
            nillable=meta.nillable,
            text=text,
            attributes=attributes,
            elements=elements,
            choices=choices,
            any_attributes=any_attributes,
            wildcards=wildcards,
            wrappers=wrappers,
        )

#### `build_vars(clazz, namespace, element_name_generator, attribute_name_generator)`

Build the binding metadata for the given dataclass fields.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`clazz` | `type` | The target class | required
`namespace` | `str | None` | The target class namespace | required
`element_name_generator` | `Callable` | The class element name generator | required
`attribute_name_generator` | `Callable` | The class attribute name generator | required

Yields:

Type | Description
--- | ---
`XmlVar` | An iterator of the field binding metadata instances.

Source code in `xsdata/formats/dataclass/models/builders.py`

    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

    def build_vars(
        self,
        clazz: type,
        namespace: str | None,
        element_name_generator: Callable,
        attribute_name_generator: Callable,
    ) -> Iterator[XmlVar]:
        """Build the binding metadata for the given dataclass fields.

        Args:
            clazz: The target class
            namespace: The target class namespace
            element_name_generator: The class element name generator
            attribute_name_generator: The class attribute name generator

        Yields:
            An iterator of the field binding metadata instances.
        """
        type_hints = get_type_hints(clazz, globalns=self.globalns)
        builder = XmlVarBuilder(
            class_type=self.class_type,
            default_xml_type=self.default_xml_type(clazz),
            element_name_generator=element_name_generator,
            attribute_name_generator=attribute_name_generator,
        )

        for field in self.class_type.get_fields(clazz):
            real_clazz = self.find_declared_class(clazz, field.name)
            globalns = sys.modules[real_clazz.__module__].__dict__
            parent_namespace = namespace
            if real_clazz is not clazz and "Meta" in real_clazz.__dict__:
                parent_namespace = getattr(real_clazz.Meta, "namespace", namespace)

            var = builder.build(
                clazz,
                field.name,
                type_hints[field.name],
                field.metadata,
                field.init,
                parent_namespace,
                self.class_type.default_value(field),
                globalns,
            )
            if var is not None:
                yield var

#### `build_class_meta(clazz, parent_namespace=None)`

Build the class meta options and merge with the defaults.

The class metaclass is not inheritable.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`clazz` | `Any` | The target class | required
`parent_namespace` | `str | None` | The parent class namespace | `None`

Returns:

Type | Description
--- | ---
`ClassMeta` | A class meta instance.

Source code in `xsdata/formats/dataclass/models/builders.py`

    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

    def build_class_meta(
        self,
        clazz: Any,
        parent_namespace: str | None = None,
    ) -> ClassMeta:
        """Build the class meta options and merge with the defaults.

        The class metaclass is not inheritable.

        Args:
            clazz: The target class
            parent_namespace: The parent class namespace

        Returns:
            A class meta instance.
        """
        meta = clazz.Meta if "Meta" in clazz.__dict__ else None
        element_name_generator = getattr(
            meta, "element_name_generator", self.element_name_generator
        )
        attribute_name_generator = getattr(
            meta, "attribute_name_generator", self.attribute_name_generator
        )
        global_type = getattr(meta, "global_type", True)
        local_name = getattr(meta, "name", None)
        local_name = local_name or element_name_generator(clazz.__name__)
        nillable = getattr(meta, "nillable", False)
        namespace = getattr(meta, "namespace", parent_namespace)
        qname = build_qname(namespace, local_name)

        if self.is_inner_class(clazz) or not global_type:
            target_qname = None
        else:
            module = sys.modules[clazz.__module__]
            target_namespace = self.target_namespace(module, meta)
            target_qname = build_qname(target_namespace, local_name)

        return ClassMeta(
            element_name_generator,
            attribute_name_generator,
            qname,
            local_name,
            nillable,
            namespace,
            target_qname,
        )

#### `find_declared_class(clazz, name)` `classmethod`

Find the user class that matches the name.

Todo: Honestly I have no idea why we needed this.

Source code in `xsdata/formats/dataclass/models/builders.py`

    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275

    @classmethod
    def find_declared_class(cls, clazz: type, name: str) -> Any:
        """Find the user class that matches the name.

        Todo: Honestly I have no idea why we needed this.
        """
        for base in clazz.__mro__:
            ann = inspect.get_annotations(base)

            if ann and name in ann:
                return base

        raise XmlContextError(f"Failed to detect the declared class for field {name}")

#### `is_inner_class(clazz)` `classmethod`

Return whether the given type is nested inside another type.

Source code in `xsdata/formats/dataclass/models/builders.py`

    277
    278
    279
    280

    @classmethod
    def is_inner_class(cls, clazz: type) -> bool:
        """Return whether the given type is nested inside another type."""
        return "." in clazz.__qualname__

#### `target_namespace(module, meta)` `classmethod`

The target namespace this class metadata was defined in.

Source code in `xsdata/formats/dataclass/models/builders.py`

    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293

    @classmethod
    def target_namespace(cls, module: Any, meta: Any) -> str | None:
        """The target namespace this class metadata was defined in."""
        namespace = getattr(meta, "target_namespace", None)
        if namespace is not None:
            return namespace

        namespace = getattr(module, "__NAMESPACE__", None)
        if namespace is not None:
            return namespace

        return getattr(meta, "namespace", None)

#### `default_xml_type(clazz)`

Return the default xml type for the fields of the given dataclass.

If a class has fields with no xml type defined, attempt to figure it from the rest of the fields. It's either a text or an element field.

##### Todo hacks like this are so unnecessary...

Source code in `xsdata/formats/dataclass/models/builders.py`

    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317

    def default_xml_type(self, clazz: type) -> str:
        """Return the default xml type for the fields of the given dataclass.

        If a class has fields with no xml type defined, attempt
        to figure it from the rest of the fields. It's either
        a text or an element field.

        # Todo hacks like this are so unnecessary...
        """
        counters: dict[str, int] = defaultdict(int)
        for var in self.class_type.get_fields(clazz):
            xml_type = var.metadata.get("type")
            counters[xml_type or "undefined"] += 1

        if counters[XmlType.TEXT] > 1:
            raise XmlContextError(
                f"Dataclass `{clazz.__name__}` includes more than one text node!"
            )

        if counters["undefined"] == 1 and counters[XmlType.TEXT] == 0:
            return XmlType.TEXT

        return XmlType.ELEMENT

### `XmlVarBuilder`

Binding class field metadata builder.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`class_type` | `ClassType` | The supported class type, e.g. dataclass, attr, pydantic | required
`default_xml_type` | `str` | The default xml type of this class fields | required
`element_name_generator` | `Callable` | The element name generator | `return_input`
`attribute_name_generator` | `Callable` | The attribute name generator | `return_input`

Attributes:

Name | Type | Description
--- | --- | ---
`index` |  | The index of the next var

Source code in `xsdata/formats/dataclass/models/builders.py`

    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

    class XmlVarBuilder:
        """Binding class field metadata builder.

        Args:
            class_type: The supported class type, e.g. dataclass, attr, pydantic
            default_xml_type: The default xml type of this class fields
            element_name_generator: The element name generator
            attribute_name_generator: The attribute name generator

        Attributes:
            index: The index of the next var
        """

        __slots__ = (
            "attribute_name_generator",
            "class_type",
            "default_xml_type",
            "element_name_generator",
            "index",
        )

        def __init__(
            self,
            class_type: ClassType,
            default_xml_type: str,
            element_name_generator: Callable = return_input,
            attribute_name_generator: Callable = return_input,
        ):
            """Initialize the builder."""
            self.index = 0
            self.class_type = class_type
            self.default_xml_type = default_xml_type
            self.element_name_generator = element_name_generator
            self.attribute_name_generator = attribute_name_generator

        def build(
            self,
            model: type,
            name: str,
            type_hint: Any,
            metadata: Mapping[str, Any],
            init: bool,
            parent_namespace: str | None,
            default_value: Any,
            globalns: Any,
            parent_factory: Callable | None = None,
        ) -> XmlVar | None:
            """Build the binding metadata for a class field.

            Args:
                model: The model class
                name: The model field name
                type_hint: The typing annotations of the field
                metadata: The field metadata mapping
                init: Specify whether this field can be initialized
                parent_namespace: The class namespace
                default_value: The field default value or factory
                globalns: Python's global namespace
                parent_factory: The value factory

            Returns:
                The field binding metadata instance.
            """
            xml_type = metadata.get("type", self.default_xml_type)
            if xml_type == XmlType.IGNORE:
                return None

            tokens = metadata.get("tokens", False)
            local_name = metadata.get("name")
            namespace = metadata.get("namespace")
            choices = metadata.get("choices", EMPTY_SEQUENCE)
            mixed = metadata.get("mixed", False)
            process_contents = metadata.get("process_contents", "strict")
            nillable = metadata.get("nillable", False)
            format_str = metadata.get("format", None)
            sequence = metadata.get("sequence", None)
            wrapper = metadata.get("wrapper", None)

            annotation = evaluate(type_hint, globalns)

            try:
                result = evaluations[xml_type](annotation, tokens=tokens)
                types = tuple(converter.sort_types(result.types))
                if not self.is_typing_supported(types):
                    raise TypeError

            except TypeError:
                raise XmlContextError(
                    f"Error on {model.__qualname__}::{name}: "
                    f"Xml {xml_type} does not support typing `{type_hint}`"
                )

            factory = result.factory or parent_factory
            tokens_factory = result.tokens_factory
            required = not result.optional
            if xml_type == XmlType.ATTRIBUTE and default_value is not None:
                required = metadata.get("required", False)
            elif tokens or factory:
                required = False

            local_name = local_name or self.build_local_name(xml_type, name)
            any_type = self.is_any_type(types, xml_type)
            clazz = first(tp for tp in types if self.class_type.is_model(tp))
            namespaces = self.resolve_namespaces(xml_type, namespace, parent_namespace)

            elements = {}
            wildcards = []
            self.index += 1
            cur_index = self.index
            for choice in self.build_choices(
                model, name, choices, factory, globalns, parent_namespace
            ):
                choice.required = False
                if choice.is_element:
                    elements[choice.qname] = choice
                else:  # choice.is_wildcard:
                    wildcards.append(choice)

            return XmlVar(
                index=cur_index,
                name=name,
                local_name=local_name,
                wrapper=wrapper,
                init=init,
                mixed=mixed,
                format=format_str,
                clazz=clazz,
                any_type=any_type,
                process_contents=process_contents,
                required=required,
                nillable=nillable,
                sequence=sequence,
                factory=factory,
                tokens_factory=tokens_factory,
                default=default_value,
                types=types,
                elements=elements,
                wildcards=wildcards,
                namespaces=namespaces,
                xml_type=xml_type,
            )

        def build_choices(
            self,
            model: type,
            name: str,
            choices: list[dict],
            factory: Callable | None,
            globalns: Any,
            parent_namespace: str | None,
        ) -> Iterator[XmlVar]:
            """Build the binding metadata for a compound dataclass field.

            Args:
                model: The model class
                name: The model field name
                choices: The list of choice metadata
                factory: The compound field values factory
                globalns: Python's global namespace
                parent_namespace: The class namespace

            Yields:
                An iterator of field choice binding metadata instance.
            """
            existing_types: set[type] = set()

            for choice in choices:
                default_value = self.class_type.default_choice_value(choice)

                metadata = choice.copy()
                metadata["name"] = choice.get("name", "any")
                type_hint = metadata["type"]

                if choice.get("wildcard"):
                    metadata["type"] = XmlType.WILDCARD
                else:
                    metadata["type"] = XmlType.ELEMENT

                var = self.build(
                    model,
                    name,
                    type_hint,
                    metadata,
                    True,
                    parent_namespace,
                    default_value,
                    globalns,
                    factory,
                )

                # It's impossible for choice elements to be ignorable, read above!
                assert var is not None

                if any(True for tp in var.types if tp in existing_types):
                    raise XmlContextError(
                        f"Error on {model.__qualname__}::{name}: "
                        f"Compound field contains ambiguous types"
                    )

                existing_types.update(var.types)

                yield var

        def build_local_name(self, xml_type: str, name: str) -> str:
            """Transform the name for serialization by the target xml type.

            Args:
                xml_type: The xml type: element, attribute, ...
                name: The field name

            Returns:
                The name to use for serialization.
            """
            if xml_type == XmlType.ATTRIBUTE:
                return self.attribute_name_generator(name)

            return self.element_name_generator(name)

        @classmethod
        def resolve_namespaces(
            cls,
            xml_type: str | None,
            namespace: str | None,
            parent_namespace: str | None,
        ) -> tuple[str, ...]:
            """Resolve a fields supported namespaces.

            Only elements and wildcards are allowed to inherit the parent
            namespace if the given namespace is empty.

            In case of wildcard try to decode the ##any, ##other, ##local,
            ##target.

            Args:
                xml_type: The xml type (Text|Element(s)|Attribute(s)|Wildcard)
                namespace: The field namespace
                parent_namespace: The parent namespace

            Returns:
                A tuple of supported namespaces.
            """
            if xml_type in (XmlType.ELEMENT, XmlType.WILDCARD) and namespace is None:
                namespace = parent_namespace

            if not namespace:
                return ()

            result = set()
            for ns in namespace.split():
                if ns == NamespaceType.TARGET_NS:
                    result.add(parent_namespace or NamespaceType.ANY_NS)
                elif ns == NamespaceType.LOCAL_NS:
                    result.add("")
                elif ns == NamespaceType.OTHER_NS:
                    result.add(f"!{parent_namespace or ''}")
                else:
                    result.add(ns)

            return tuple(result)

        @classmethod
        def is_any_type(cls, types: Sequence[type], xml_type: str) -> bool:
            """Return whether the given xml type supports generic values."""
            if xml_type in (XmlType.ELEMENT, XmlType.ELEMENTS):
                return object in types

            return False

        def is_typing_supported(self, types: Sequence[type]) -> bool:
            """Validate all types are registered in the converter."""
            for tp in types:
                if (
                    not self.class_type.is_model(tp)
                    and tp not in converter.registry
                    and not issubclass(tp, Enum)
                ):
                    return False

            return True

#### `__init__(class_type, default_xml_type, element_name_generator=return_input, attribute_name_generator=return_input)`

Initialize the builder.

Source code in `xsdata/formats/dataclass/models/builders.py`

    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353

    def __init__(
        self,
        class_type: ClassType,
        default_xml_type: str,
        element_name_generator: Callable = return_input,
        attribute_name_generator: Callable = return_input,
    ):
        """Initialize the builder."""
        self.index = 0
        self.class_type = class_type
        self.default_xml_type = default_xml_type
        self.element_name_generator = element_name_generator
        self.attribute_name_generator = attribute_name_generator

#### `build(model, name, type_hint, metadata, init, parent_namespace, default_value, globalns, parent_factory=None)`

Build the binding metadata for a class field.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`model` | `type` | The model class | required
`name` | `str` | The model field name | required
`type_hint` | `Any` | The typing annotations of the field | required
`metadata` | `Mapping[str, Any]` | The field metadata mapping | required
`init` | `bool` | Specify whether this field can be initialized | required
`parent_namespace` | `str | None` | The class namespace | required
`default_value` | `Any` | The field default value or factory | required
`globalns` | `Any` | Python's global namespace | required
`parent_factory` | `Callable | None` | The value factory | `None`

Returns:

Type | Description
--- | ---
`XmlVar | None` | The field binding metadata instance.

Source code in `xsdata/formats/dataclass/models/builders.py`

    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

    def build(
        self,
        model: type,
        name: str,
        type_hint: Any,
        metadata: Mapping[str, Any],
        init: bool,
        parent_namespace: str | None,
        default_value: Any,
        globalns: Any,
        parent_factory: Callable | None = None,
    ) -> XmlVar | None:
        """Build the binding metadata for a class field.

        Args:
            model: The model class
            name: The model field name
            type_hint: The typing annotations of the field
            metadata: The field metadata mapping
            init: Specify whether this field can be initialized
            parent_namespace: The class namespace
            default_value: The field default value or factory
            globalns: Python's global namespace
            parent_factory: The value factory

        Returns:
            The field binding metadata instance.
        """
        xml_type = metadata.get("type", self.default_xml_type)
        if xml_type == XmlType.IGNORE:
            return None

        tokens = metadata.get("tokens", False)
        local_name = metadata.get("name")
        namespace = metadata.get("namespace")
        choices = metadata.get("choices", EMPTY_SEQUENCE)
        mixed = metadata.get("mixed", False)
        process_contents = metadata.get("process_contents", "strict")
        nillable = metadata.get("nillable", False)
        format_str = metadata.get("format", None)
        sequence = metadata.get("sequence", None)
        wrapper = metadata.get("wrapper", None)

        annotation = evaluate(type_hint, globalns)

        try:
            result = evaluations[xml_type](annotation, tokens=tokens)
            types = tuple(converter.sort_types(result.types))
            if not self.is_typing_supported(types):
                raise TypeError

        except TypeError:
            raise XmlContextError(
                f"Error on {model.__qualname__}::{name}: "
                f"Xml {xml_type} does not support typing `{type_hint}`"
            )

        factory = result.factory or parent_factory
        tokens_factory = result.tokens_factory
        required = not result.optional
        if xml_type == XmlType.ATTRIBUTE and default_value is not None:
            required = metadata.get("required", False)
        elif tokens or factory:
            required = False

        local_name = local_name or self.build_local_name(xml_type, name)
        any_type = self.is_any_type(types, xml_type)
        clazz = first(tp for tp in types if self.class_type.is_model(tp))
        namespaces = self.resolve_namespaces(xml_type, namespace, parent_namespace)

        elements = {}
        wildcards = []
        self.index += 1
        cur_index = self.index
        for choice in self.build_choices(
            model, name, choices, factory, globalns, parent_namespace
        ):
            choice.required = False
            if choice.is_element:
                elements[choice.qname] = choice
            else:  # choice.is_wildcard:
                wildcards.append(choice)

        return XmlVar(
            index=cur_index,
            name=name,
            local_name=local_name,
            wrapper=wrapper,
            init=init,
            mixed=mixed,
            format=format_str,
            clazz=clazz,
            any_type=any_type,
            process_contents=process_contents,
            required=required,
            nillable=nillable,
            sequence=sequence,
            factory=factory,
            tokens_factory=tokens_factory,
            default=default_value,
            types=types,
            elements=elements,
            wildcards=wildcards,
            namespaces=namespaces,
            xml_type=xml_type,
        )

#### `build_choices(model, name, choices, factory, globalns, parent_namespace)`

Build the binding metadata for a compound dataclass field.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`model` | `type` | The model class | required
`name` | `str` | The model field name | required
`choices` | `list[dict]` | The list of choice metadata | required
`factory` | `Callable | None` | The compound field values factory | required
`globalns` | `Any` | Python's global namespace | required
`parent_namespace` | `str | None` | The class namespace | required

Yields:

Type | Description
--- | ---
`XmlVar` | An iterator of field choice binding metadata instance.

Source code in `xsdata/formats/dataclass/models/builders.py`

    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

    def build_choices(
        self,
        model: type,
        name: str,
        choices: list[dict],
        factory: Callable | None,
        globalns: Any,
        parent_namespace: str | None,
    ) -> Iterator[XmlVar]:
        """Build the binding metadata for a compound dataclass field.

        Args:
            model: The model class
            name: The model field name
            choices: The list of choice metadata
            factory: The compound field values factory
            globalns: Python's global namespace
            parent_namespace: The class namespace

        Yields:
            An iterator of field choice binding metadata instance.
        """
        existing_types: set[type] = set()

        for choice in choices:
            default_value = self.class_type.default_choice_value(choice)

            metadata = choice.copy()
            metadata["name"] = choice.get("name", "any")
            type_hint = metadata["type"]

            if choice.get("wildcard"):
                metadata["type"] = XmlType.WILDCARD
            else:
                metadata["type"] = XmlType.ELEMENT

            var = self.build(
                model,
                name,
                type_hint,
                metadata,
                True,
                parent_namespace,
                default_value,
                globalns,
                factory,
            )

            # It's impossible for choice elements to be ignorable, read above!
            assert var is not None

            if any(True for tp in var.types if tp in existing_types):
                raise XmlContextError(
                    f"Error on {model.__qualname__}::{name}: "
                    f"Compound field contains ambiguous types"
                )

            existing_types.update(var.types)

            yield var

#### `build_local_name(xml_type, name)`

Transform the name for serialization by the target xml type.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`xml_type` | `str` | The xml type: element, attribute, ... | required
`name` | `str` | The field name | required

Returns:

Type | Description
--- | ---
`str` | The name to use for serialization.

Source code in `xsdata/formats/dataclass/models/builders.py`

    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536

    def build_local_name(self, xml_type: str, name: str) -> str:
        """Transform the name for serialization by the target xml type.

        Args:
            xml_type: The xml type: element, attribute, ...
            name: The field name

        Returns:
            The name to use for serialization.
        """
        if xml_type == XmlType.ATTRIBUTE:
            return self.attribute_name_generator(name)

        return self.element_name_generator(name)

#### `resolve_namespaces(xml_type, namespace, parent_namespace)` `classmethod`

Resolve a fields supported namespaces.

Only elements and wildcards are allowed to inherit the parent namespace if the given namespace is empty.

In case of wildcard try to decode the ##any, ##other, ##local,

###### target.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`xml_type` | `str | None` | The xml type (Text|Element(s)|Attribute(s)|Wildcard) | required
`namespace` | `str | None` | The field namespace | required
`parent_namespace` | `str | None` | The parent namespace | required

Returns:

Type | Description
--- | ---
`tuple[str, ...]` | A tuple of supported namespaces.

Source code in `xsdata/formats/dataclass/models/builders.py`

    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

    @classmethod
    def resolve_namespaces(
        cls,
        xml_type: str | None,
        namespace: str | None,
        parent_namespace: str | None,
    ) -> tuple[str, ...]:
        """Resolve a fields supported namespaces.

        Only elements and wildcards are allowed to inherit the parent
        namespace if the given namespace is empty.

        In case of wildcard try to decode the ##any, ##other, ##local,
        ##target.

        Args:
            xml_type: The xml type (Text|Element(s)|Attribute(s)|Wildcard)
            namespace: The field namespace
            parent_namespace: The parent namespace

        Returns:
            A tuple of supported namespaces.
        """
        if xml_type in (XmlType.ELEMENT, XmlType.WILDCARD) and namespace is None:
            namespace = parent_namespace

        if not namespace:
            return ()

        result = set()
        for ns in namespace.split():
            if ns == NamespaceType.TARGET_NS:
                result.add(parent_namespace or NamespaceType.ANY_NS)
            elif ns == NamespaceType.LOCAL_NS:
                result.add("")
            elif ns == NamespaceType.OTHER_NS:
                result.add(f"!{parent_namespace or ''}")
            else:
                result.add(ns)

        return tuple(result)

#### `is_any_type(types, xml_type)` `classmethod`

Return whether the given xml type supports generic values.

Source code in `xsdata/formats/dataclass/models/builders.py`

    580
    581
    582
    583
    584
    585
    586

    @classmethod
    def is_any_type(cls, types: Sequence[type], xml_type: str) -> bool:
        """Return whether the given xml type supports generic values."""
        if xml_type in (XmlType.ELEMENT, XmlType.ELEMENTS):
            return object in types

        return False

#### `is_typing_supported(types)`

Validate all types are registered in the converter.

Source code in `xsdata/formats/dataclass/models/builders.py`

    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598

    def is_typing_supported(self, types: Sequence[type]) -> bool:
        """Validate all types are registered in the converter."""
        for tp in types:
            if (
                not self.class_type.is_model(tp)
                and tp not in converter.registry
                and not issubclass(tp, Enum)
            ):
                return False

        return True