Skip to content

Forward referencing when generating a recursive schema #882

@SrirachaHorse

Description

@SrirachaHorse

If generating a recursively defined schema with postponed annotations and compound fields both enabled, the library does not correctly forward reference the recursive types, leading to generated code that cannot be used or imported.

Consider this schema where two types reference each other.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:complexType name="type1">
        <xs:sequence>
            <xs:choice>
                <xs:element name="str" type="xs:string"/>
                <xs:element name="data" type="type2"/>
            </xs:choice>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="type2">
        <xs:sequence>
            <xs:choice>
                <xs:element name="str" type="xs:string"/>
                <xs:element name="data" type="type1"/>
            </xs:choice>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

If generating this schema with xsdata generate schema.xsd --compound-fields --postponed-annotations, then the following dataclass is produced:

from __future__ import annotations
from dataclasses import dataclass, field
from typing import Optional, Union


@dataclass
class Type2:
    class Meta:
        name = "type2"

    str_or_data: Optional[Union[str, Type1]] = field(
        default=None,
        metadata={
            "type": "Elements",
            "choices": (
                {
                    "name": "str",
                    "type": str,
                    "namespace": "",
                },
                {
                    "name": "data",
                    "type": Type1,
                    "namespace": "",
                },
            ),
        }
    )


@dataclass
class Type1:
    class Meta:
        name = "type1"

    str_or_data: Optional[Union[str, Type2]] = field(
        default=None,
        metadata={
            "type": "Elements",
            "choices": (
                {
                    "name": "str",
                    "type": str,
                    "namespace": "",
                },
                {
                    "name": "data",
                    "type": Type2,
                    "namespace": "",
                },
            ),
        }
    )

The reference to Type1 in the choices dict of Type2's metadata is invalid, as it is a forward reference that is yet to be evaluated by the Python interpreter. This leads to invalid Python code being generated and the dataclasses are unable to be imported:

>>> import generated
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/test/generated/__init__.py", line 1, in <module>
    from generated.schema import (
  File "/test/generated/schema.py", line 7, in <module>
    class Type2:
  File "/test/generated/schema.py", line 23, in Type2
    "type": Type1,
NameError: name 'Type1' is not defined

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions