Skip to content

element

xsdata.codegen.mappers.element

ElementMapper

Bases: RawDocumentMapper

Map a generic element to classes.

This mapper is used to build classes from raw xml documents.

Source code in xsdata/codegen/mappers/element.py
 13
 14
 15
 16
 17
 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
class ElementMapper(RawDocumentMapper):
    """Map a generic element to classes.

    This mapper is used to build classes from raw xml documents.
    """

    @classmethod
    def map(cls, element: AnyElement, location: str) -> List[Class]:
        """Map schema children elements to classes.

        Args:
            element: The root element to be mapped
            location: The location of the xml document

        Returns:
            The list of mapped class instances.
        """
        assert element.qname is not None

        uri, name = split_qname(element.qname)
        target = cls.build_class(element, uri)

        return list(ClassUtils.flatten(target, f"{location}/{name}"))

    @classmethod
    def build_class(cls, element: AnyElement, parent_namespace: Optional[str]) -> Class:
        """Build a Class instance for the given generic element.

        Args:
            element: The generic element to be mapped
            parent_namespace: The parent element namespace

        Returns:
            The mapped class instance.
        """
        assert element.qname is not None

        namespace, name = split_qname(element.qname)
        namespace = cls.select_namespace(namespace, parent_namespace)
        target = Class(
            qname=build_qname(namespace, name),
            namespace=namespace,
            tag=Tag.ELEMENT,
            location="",
        )

        cls.build_attributes(target, element, namespace)
        cls.build_elements(target, element, namespace)
        cls.build_text(target, element)

        return target

    @classmethod
    def build_attributes(
        cls,
        target: Class,
        element: AnyElement,
        namespace: Optional[str],
    ):
        """Build attributes for the given Class instance based on AnyElement attributes.

        Args:
            target: The target class instance
            element: The AnyElement containing attributes.
            namespace: The namespace.

        """
        for key, value in element.attributes.items():
            if key == QNames.XSI_NIL:
                target.nillable = value.strip() in ("true", "1")
            else:
                attr_type = cls.build_attr_type(key, value)
                cls.build_attr(target, key, attr_type, namespace, Tag.ATTRIBUTE)

    @classmethod
    def build_elements(
        cls,
        target: Class,
        element: AnyElement,
        namespace: Optional[str],
    ):
        """Build elements for the given Class instance based on AnyElement children.

        Args:
            target: The target class instance
            element: The AnyElement containing children.
            namespace: The namespace.
        """
        sequences = cls.sequential_groups(element)
        for index, child in enumerate(element.children):
            if isinstance(child, AnyElement) and child.qname:
                if child.tail:
                    target.mixed = True

                if child.attributes or child.children:
                    inner = cls.build_class(child, namespace)
                    inner.parent = target
                    attr_type = AttrType(qname=inner.qname, forward=True)
                    target.inner.append(inner)
                else:
                    attr_type = cls.build_attr_type(child.qname, child.text)

                sequence = collections.find_connected_component(sequences, index)
                cls.build_attr(
                    target,
                    child.qname,
                    attr_type,
                    namespace,
                    Tag.ELEMENT,
                    sequence + 1,
                )

    @classmethod
    def build_text(cls, target: Class, element: AnyElement):
        """Build a text attr from the generic element text value.

        Args:
            target: The target class instance
            element: The AnyElement containing text content.
        """
        if element.text:
            attr_type = cls.build_attr_type("value", element.text)
            cls.build_attr(target, "value", attr_type, None, Tag.SIMPLE_TYPE)

            if any(attr.tag == Tag.ELEMENT for attr in target.attrs):
                target.mixed = True

    @classmethod
    def sequential_groups(cls, element: AnyElement) -> List[List[int]]:
        """Identify sequential groups of repeating attributes.

        Args:
            element: The generic element instance

        Returns:
            A list of lists of strongly connected children indexes.
        """
        groups = cls.group_repeating_attrs(element)
        return list(collections.connected_components(groups))

    @classmethod
    def group_repeating_attrs(cls, element: AnyElement) -> List[List[int]]:
        """Group repeating children in the given generic element.

        Args:
            element: The generic element instance

        Returns:
            A list of lists of children indexes.
        """
        counters = defaultdict(list)
        for index, child in enumerate(element.children):
            if isinstance(child, AnyElement) and child.qname:
                counters[child.qname].append(index)

        groups = []
        if len(counters) > 1:
            for x in counters.values():
                if len(x) > 1:
                    groups.append(list(range(x[0], x[-1] + 1)))

        return groups

map(element, location) classmethod

Map schema children elements to classes.

Parameters:

Name Type Description Default
element AnyElement

The root element to be mapped

required
location str

The location of the xml document

required

Returns:

Type Description
List[Class]

The list of mapped class instances.

Source code in xsdata/codegen/mappers/element.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@classmethod
def map(cls, element: AnyElement, location: str) -> List[Class]:
    """Map schema children elements to classes.

    Args:
        element: The root element to be mapped
        location: The location of the xml document

    Returns:
        The list of mapped class instances.
    """
    assert element.qname is not None

    uri, name = split_qname(element.qname)
    target = cls.build_class(element, uri)

    return list(ClassUtils.flatten(target, f"{location}/{name}"))

build_class(element, parent_namespace) classmethod

Build a Class instance for the given generic element.

Parameters:

Name Type Description Default
element AnyElement

The generic element to be mapped

required
parent_namespace Optional[str]

The parent element namespace

required

Returns:

Type Description
Class

The mapped class instance.

Source code in xsdata/codegen/mappers/element.py
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
@classmethod
def build_class(cls, element: AnyElement, parent_namespace: Optional[str]) -> Class:
    """Build a Class instance for the given generic element.

    Args:
        element: The generic element to be mapped
        parent_namespace: The parent element namespace

    Returns:
        The mapped class instance.
    """
    assert element.qname is not None

    namespace, name = split_qname(element.qname)
    namespace = cls.select_namespace(namespace, parent_namespace)
    target = Class(
        qname=build_qname(namespace, name),
        namespace=namespace,
        tag=Tag.ELEMENT,
        location="",
    )

    cls.build_attributes(target, element, namespace)
    cls.build_elements(target, element, namespace)
    cls.build_text(target, element)

    return target

build_attributes(target, element, namespace) classmethod

Build attributes for the given Class instance based on AnyElement attributes.

Parameters:

Name Type Description Default
target Class

The target class instance

required
element AnyElement

The AnyElement containing attributes.

required
namespace Optional[str]

The namespace.

required
Source code in xsdata/codegen/mappers/element.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@classmethod
def build_attributes(
    cls,
    target: Class,
    element: AnyElement,
    namespace: Optional[str],
):
    """Build attributes for the given Class instance based on AnyElement attributes.

    Args:
        target: The target class instance
        element: The AnyElement containing attributes.
        namespace: The namespace.

    """
    for key, value in element.attributes.items():
        if key == QNames.XSI_NIL:
            target.nillable = value.strip() in ("true", "1")
        else:
            attr_type = cls.build_attr_type(key, value)
            cls.build_attr(target, key, attr_type, namespace, Tag.ATTRIBUTE)

build_elements(target, element, namespace) classmethod

Build elements for the given Class instance based on AnyElement children.

Parameters:

Name Type Description Default
target Class

The target class instance

required
element AnyElement

The AnyElement containing children.

required
namespace Optional[str]

The namespace.

required
Source code in xsdata/codegen/mappers/element.py
 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
@classmethod
def build_elements(
    cls,
    target: Class,
    element: AnyElement,
    namespace: Optional[str],
):
    """Build elements for the given Class instance based on AnyElement children.

    Args:
        target: The target class instance
        element: The AnyElement containing children.
        namespace: The namespace.
    """
    sequences = cls.sequential_groups(element)
    for index, child in enumerate(element.children):
        if isinstance(child, AnyElement) and child.qname:
            if child.tail:
                target.mixed = True

            if child.attributes or child.children:
                inner = cls.build_class(child, namespace)
                inner.parent = target
                attr_type = AttrType(qname=inner.qname, forward=True)
                target.inner.append(inner)
            else:
                attr_type = cls.build_attr_type(child.qname, child.text)

            sequence = collections.find_connected_component(sequences, index)
            cls.build_attr(
                target,
                child.qname,
                attr_type,
                namespace,
                Tag.ELEMENT,
                sequence + 1,
            )

build_text(target, element) classmethod

Build a text attr from the generic element text value.

Parameters:

Name Type Description Default
target Class

The target class instance

required
element AnyElement

The AnyElement containing text content.

required
Source code in xsdata/codegen/mappers/element.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
@classmethod
def build_text(cls, target: Class, element: AnyElement):
    """Build a text attr from the generic element text value.

    Args:
        target: The target class instance
        element: The AnyElement containing text content.
    """
    if element.text:
        attr_type = cls.build_attr_type("value", element.text)
        cls.build_attr(target, "value", attr_type, None, Tag.SIMPLE_TYPE)

        if any(attr.tag == Tag.ELEMENT for attr in target.attrs):
            target.mixed = True

sequential_groups(element) classmethod

Identify sequential groups of repeating attributes.

Parameters:

Name Type Description Default
element AnyElement

The generic element instance

required

Returns:

Type Description
List[List[int]]

A list of lists of strongly connected children indexes.

Source code in xsdata/codegen/mappers/element.py
140
141
142
143
144
145
146
147
148
149
150
151
@classmethod
def sequential_groups(cls, element: AnyElement) -> List[List[int]]:
    """Identify sequential groups of repeating attributes.

    Args:
        element: The generic element instance

    Returns:
        A list of lists of strongly connected children indexes.
    """
    groups = cls.group_repeating_attrs(element)
    return list(collections.connected_components(groups))

group_repeating_attrs(element) classmethod

Group repeating children in the given generic element.

Parameters:

Name Type Description Default
element AnyElement

The generic element instance

required

Returns:

Type Description
List[List[int]]

A list of lists of children indexes.

Source code in xsdata/codegen/mappers/element.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
@classmethod
def group_repeating_attrs(cls, element: AnyElement) -> List[List[int]]:
    """Group repeating children in the given generic element.

    Args:
        element: The generic element instance

    Returns:
        A list of lists of children indexes.
    """
    counters = defaultdict(list)
    for index, child in enumerate(element.children):
        if isinstance(child, AnyElement) and child.qname:
            counters[child.qname].append(index)

    groups = []
    if len(counters) > 1:
        for x in counters.values():
            if len(x) > 1:
                groups.append(list(range(x[0], x[-1] + 1)))

    return groups