tefra / xsdata

Naive XML & JSON Bindings for python
https://xsdata.readthedocs.io
MIT License
310 stars 56 forks source link

Cannot generate nested group references with --unnest-classes #1046

Closed SrirachaHorse closed 1 month ago

SrirachaHorse commented 2 months ago

When using xsdata 24.5, nested group references that are placed in a complex type are unable to be generated when using --unnest-classes. If --unnest-classes is not provided, the classes are successfully generated.

This is a regression from 24.4, where dataclasses can be generated from the same schema. Based on a git bisect, the regression was introduced by #1016.

Below is an example schema, and the error produced when using --unnest-classes with 24.5. The dataclasses produced by 24.4 are also provided for comparison.

Example schema:

<?xml version="1.0" encoding="utf-8"/>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:group name="group1">
        <xs:sequence>
            <xs:element name="field1" type="xs:string" />
            <xs:element name="nested1">
                <xs:complexType>
                    <xs:group ref="group1" />
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:group>

    <xs:complexType name="type1">
        <xs:sequence>
            <xs:element name="field2" type="xs:string" />
            <xs:group ref="group1" />
        </xs:sequence>
    </xs:complexType>
</xs:schema>

Error using 24.5 (xsdata generate schema.xsd --unnest-classes):

========= xsdata v24.5 / Python 3.8.13 / Platform linux =========

Parsing schema file:///testing/schema.xsd
Compiling schema file:///testing/schema.xsd
Builder: 2 main and 1 inner classes
Analyzer input: 2 main and 1 inner classes
=========
Error: Missing inner class
parent: Class(qname='type1_nested1', tag='ComplexType', location='file:///testing/schema.xsd', mixed=False, abstract=False, nillable=False, local_type=True, status=<Status.FLATTENING: 20>, container='Element', package=None, module=None, namespace=None, help=None, meta_name=None, default=None, fixed=False, substitutions=[], extensions=[], attrs=[Attr(tag='Element', name='field1', local_name='field1', wrapper=None, index=4, default=None, fixed=False, mixed=False, types=[AttrType(qname='{http://www.w3.org/2001/XMLSchema}string', alias=None, reference=0, native=True, forward=False, circular=False, substituted=True)], choices=[], namespace='', help=None, restrictions=Restrictions(min_occurs=1, max_occurs=1, min_exclusive=None, min_inclusive=None, min_length=None, max_exclusive=None, max_inclusive=None, max_length=None, total_digits=None, fraction_digits=None, length=None, white_space=None, pattern=None, explicit_timezone=None, nillable=None, sequence=139894606880928, tokens=None, format=None, choice=None, group=139894607167440, process_contents=None, path=[('g', 139894607611312, 1, 1), ('g', 139894607167440, 1, 1), ('s', 139894606880928, 1, 1)]), parent=None, substitution=None), Attr(tag='Element', name='nested1', local_name='nested1', wrapper=None, index=5, default=None, fixed=False, mixed=False, types=[AttrType(qname='nested1', alias=None, reference=0, native=False, forward=True, circular=False, substituted=True)], choices=[], namespace='', help=None, restrictions=Restrictions(min_occurs=1, max_occurs=1, min_exclusive=None, min_inclusive=None, min_length=None, max_exclusive=None, max_inclusive=None, max_length=None, total_digits=None, fraction_digits=None, length=None, white_space=None, pattern=None, explicit_timezone=None, nillable=None, sequence=139894606880928, tokens=None, format=None, choice=None, group=139894607167440, process_contents=None, path=[('g', 139894607611312, 1, 1), ('g', 139894607167440, 1, 1), ('s', 139894606880928, 1, 1)]), parent=None, substitution=None)], inner=[], ns_map={'xs': 'http://www.w3.org/2001/XMLSchema', 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', 'xml': 'http://www.w3.org/XML/1998/namespace', 'xlink': 'http://www.w3.org/1999/xlink'}, parent=None)
qname: nested1

Output dataclasses in 24.4 (xsdata generate schema.xsd --unnest-classes):

from dataclasses import dataclass, field
from typing import Optional

@dataclass
class Type1Nested1:
    class Meta:
        global_type = False

    field1: Optional[str] = field(
        default=None,
        metadata={
            "type": "Element",
            "namespace": "",
            "required": True,
        },
    )
    nested1: Optional["Type1Nested1"] = field(
        default=None,
        metadata={
            "type": "Element",
            "namespace": "",
            "required": True,
        },
    )

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

    field2: Optional[str] = field(
        default=None,
        metadata={
            "type": "Element",
            "namespace": "",
            "required": True,
        },
    )
    field1: Optional[str] = field(
        default=None,
        metadata={
            "type": "Element",
            "namespace": "",
            "required": True,
        },
    )
    nested1: Optional[Type1Nested1] = field(
        default=None,
        metadata={
            "type": "Element",
            "namespace": "",
            "required": True,
        },
    )
tefra commented 1 month ago

Thanks for reporting @SrirachaHorse the fix is on main!