Issue in data model / xs:choice
On following testcase:
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,
},
)
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
For repeating choices, or complex sequence elements give compound fields a try, it's very tough to figure these out with standalone fields.
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.