xsdata icon indicating copy to clipboard operation
xsdata copied to clipboard

Issue in data model / xs:choice

Open Thomasb81 opened this issue 8 months ago • 3 comments

test.xsd.zip

On following testcase:

Image

using xsdata, version 24.12

the generate test.py containt:

        linker_command_file: list[LinkerCommandFile] = field(
            default_factory=list,
            metadata={
                "name": "linkerCommandFile",
                "type": "Element",
                "max_occurs": 2,
                "sequence": 1,
            },
        )

max_occures = 2 is inaccurate according to what the schema describe. It seems the tool is adding the testns:linkCommandFile contribution of the choice instead of the max. For first option min_occur is 0 max_occur is 1. For second option max occur is 1, max_occur is 1

							<xs:sequence minOccurs="0">
								<xs:annotation>
									<xs:documentation/>
								</xs:annotation>
								<xs:element name="linker" type="xs:string" minOccurs="1"/>
								<xs:choice>
									<xs:sequence minOccurs="1">
										<xs:element name="linkerFlags" type="xs:string"
											minOccurs="1"/>
										<xs:element ref="testns:linkerCommandFile" minOccurs="0"/>
									</xs:sequence>
									<xs:element ref="testns:linkerCommandFile" minOccurs="1"/>
								</xs:choice>
							</xs:sequence>

I guess the correct statement would be:

       linker_command_file: list[LinkerCommandFile] = field(
           default_factory=list,
           metadata={
               "name": "linkerCommandFile",
               "type": "Element",
               "max_occurs": 1,
               "sequence": 1,
           },
       )

Thomasb81 avatar Apr 20 '25 16:04 Thomasb81

Hello, I noticed similar issue with IPXact schema.

Simplified version of problematic element:

			<xs:sequence>
				<xs:group ref="ipxact:nameGroup"/>
                <xs:element ref="ipxact:activeInterface"/>
				<xs:choice>
					<xs:sequence>
						<xs:element ref="ipxact:activeInterface" maxOccurs="unbounded"/>
						<xs:element name="hierInterface" type="ipxact:hierInterfaceType" minOccurs="0" maxOccurs="unbounded"/>
					</xs:sequence>
					<xs:element name="hierInterface" type="ipxact:hierInterfaceType" maxOccurs="unbounded"/>
				</xs:choice>
				<xs:element ref="ipxact:vendorExtensions" minOccurs="0"/>
			</xs:sequence>

When converted:

    active_interface: list[ActiveInterface] = field(
        default_factory=list,
        metadata={
            "name": "activeInterface",
            "type": "Element",
            "min_occurs": 2,
            "sequence": 1,
        },
    )
    hier_interface: list[HierInterfaceType] = field(
        default_factory=list,
        metadata={
            "name": "hierInterface",
            "type": "Element",
            "min_occurs": 1,
            "sequence": 1,
        },
    )

Where min_occurs is 2 for active_interface instead of 1

Risto97 avatar Apr 25 '25 14:04 Risto97

For repeating choices, or complex sequence elements give compound fields a try, it's very tough to figure these out with standalone fields.

tefra avatar May 02 '25 12:05 tefra

Hi

I give a trial to compound_fields. I don't fill comfortable with the feature. It complexity the equivalence between the xml path of element and the python data structure hierarchy. Which is not welcome for my targeted application.

I prefer to deal with choice differently : adding a dict to the dataclass that store the which field are exclusive seems a better option for me.

Ie:

 	<xs:complexType name="writeValueConstraintType">
		<xs:annotation>
			<xs:documentation>A constraint on the values that can be written to this field. Absence of this element implies that any value that fits can be written to it.</xs:documentation>
		</xs:annotation>
		<xs:choice>
			<xs:element name="writeAsRead" type="xs:boolean">
				<xs:annotation>
					<xs:documentation>writeAsRead indicates that only a value immediately read before a write is a legal value to be written.</xs:documentation>
				</xs:annotation>
			</xs:element>
			<xs:element name="useEnumeratedValues" type="xs:boolean">
				<xs:annotation>
					<xs:documentation>useEnumeratedValues indicates that only write enumeration value shall be legal values to be written.</xs:documentation>
				</xs:annotation>
			</xs:element>
			<xs:sequence>
				<xs:element name="minimum" type="unsignedBitVectorExpression">
					<xs:annotation>
						<xs:documentation>The minimum legal value that may be written to a field</xs:documentation>
					</xs:annotation>
				</xs:element>
				<xs:element name="maximum" type="unsignedBitVectorExpression">
					<xs:annotation>
						<xs:documentation>The maximum legal value that may be written to a field</xs:documentation>
					</xs:annotation>
				</xs:element>
			</xs:sequence>
		</xs:choice>
	</xs:complexType>

with help of an xsdata plugin I produce:

@dataclass
class WriteValueConstraintType:
[...]

    class Meta:
        name = "writeValueConstraintType"
        exclusion = {
            "writeAsRead": ["useEnumeratedValues", "minimum", "maximum"],
            "useEnumeratedValues": ["writeAsRead", "minimum", "maximum"],
            "minimum": ["writeAsRead", "useEnumeratedValues"],
            "maximum": ["writeAsRead", "useEnumeratedValues"],
        }

    writeAsRead: Optional[bool] = field(
        default=None,
        metadata={
            "type": "Element",
        },
    )
    useEnumeratedValues: Optional[bool] = field(
        default=None,
        metadata={
            "type": "Element",
        },
    )
    minimum: Optional[UnsignedBitVectorExpression] = field(
        default=None,
        metadata={
            "type": "Element",
        },
    )
    maximum: Optional[UnsignedBitVectorExpression] = field(
        default=None,
        metadata={
            "type": "Element",
        },
    )

I use this exclusion dict this way: In case I need to add minimum field to a WriteValueConstraintType object, I will check and remove writeAsRead and useEnumeratedValues if present.

I can then continue to use WriteValueContraintType object with following construction <objectVar>.minimum. The python data structure remain then similar to the xml data as lxml.objectify library propose. only exception I need to handle is when xsdata decide he cannot use the element name for a field.

In this usage having correct value for min and max occurrence has still some interest.

Thomasb81 avatar May 03 '25 08:05 Thomasb81