Choice elements not generating correctly when minOccurs=1 and maxOccurs=1
Using the compound fields option, the following XML:
<xs:complexType name="DeemedRateType2Choice">
<xs:choice minOccurs="1" maxOccurs="1">
<xs:element name="Cd" type="DeemedRateType1Code"/>
<xs:element name="Prtry" type="GenericIdentification47"/>
</xs:choice>
</xs:complexType>
generates
@dataclass
class DeemedRateType2Choice:
cd: Optional[DeemedRateType1Code] = field(
default=None,
metadata={
"name": "Cd",
"type": "Element",
"namespace": "urn:iso:std:iso:20022:tech:xsd:seev.036.002.09",
}
)
prtry: Optional[GenericIdentification47] = field(
default=None,
metadata={
"name": "Prtry",
"type": "Element",
"namespace": "urn:iso:std:iso:20022:tech:xsd:seev.036.002.09",
}
)
and I think it should be something like:
@dataclass
class DeemedRateType2Choice:
cd_or_prtry: object = field(
metadata={
"type": "Elements",
"choices": (
{
"name": "Cd",
"type": DeemedRateType1Code,
"namespace": "urn:iso:std:iso:20022:tech:xsd:seev.036.002.09",
},
{
"name": "Prtry",
"type": GenericIdentification47,
"namespace": "urn:iso:std:iso:20022:tech:xsd:seev.036.002.09",
},
),
required=True
}
)
Note that if i change the xml to minOccurs=2 and maxOccurs=2 then I get
@dataclass
class DeemedRateType2Choice:
cd_or_prtry: List[object] = field(
default_factory=list,
metadata={
"type": "Elements",
"choices": (
{
"name": "Cd",
"type": DeemedRateType1Code,
"namespace": "urn:iso:std:iso:20022:tech:xsd:seev.036.002.09",
},
{
"name": "Prtry",
"type": GenericIdentification47,
"namespace": "urn:iso:std:iso:20022:tech:xsd:seev.036.002.09",
},
),
"min_occurs": 2,
"max_occurs": 2,
}
)
This output would be fine too, as long as it's not dropping the min_occurs=1 and max_occurs=1 information.
hi @dziegler
compound fields were introduced in order to achieve repeatable choice elements that can occur in any order
<a />
<a />
<b />
<a />
Personally I don't like compound fields but it's a necessary evil otherwise maintaining elements ordering is impossible between rountrips. With that in mind I tried to minimize their effect only when the repeatable choice has maxOccurs > 1, otherwise the generator is not grouping the properties but it is forcing all sub-elements to be optional.
It makes sense to me, but I am open to suggestions why do you think it's the wrong approach?
The xsd is saying that DeemedRateType2Choice should have one and only one child, and that child can be either DeemedRateType1Code or GenericIdentification47. The generated dataclass doesn't doesn't include any of these restrictions or include any metadata that would allow me to enforce that restriction. A DeemedRateType2Choice instance can end up looking like:
{
"cd": [cd_obj1, cd_obj2],
"prtry": [prtry_obj1, prtry_obj2]
}
or
{
"cd": [],
"prtry": []
}
which will fail to validate against the schema.
It should really be something like
{
"cd_or_prtry": cd_obj1
}
or
{
"cd": [cd_obj1],
"prtry": []
}
with some metadata that would allow me to enforce a min/max count
Also the default value for maxOccurs and minOccurs is 1: https://www.w3.org/TR/xmlschema-0/#ref6 so i would expect that
<xs:complexType name="DeemedRateType2Choice">
<xs:choice minOccurs="1" maxOccurs="1">
<xs:element name="Cd" type="DeemedRateType1Code"/>
<xs:element name="Prtry" type="GenericIdentification47"/>
</xs:choice>
</xs:complexType>
behaves the same as
<xs:complexType name="DeemedRateType2Choice">
<xs:choice>
<xs:element name="Cd" type="DeemedRateType1Code"/>
<xs:element name="Prtry" type="GenericIdentification47"/>
</xs:choice>
</xs:complexType>
Also the default value for maxOccurs and minOccurs is 1: https://www.w3.org/TR/xmlschema-0/#ref6 so i would expect that
It's modeled liked that in xsdata as well https://github.com/tefra/xsdata/blob/6fba8384410088f11ff4accbd5b86e51b947d8fe/xsdata/models/xsd.py#L484-L498
The Restrictions class is like the computed restrictions of a field after the initial parsing, that can change during the analyzing process, specifically for the compound fields the handler decides not to group those fields, if there is no field with maxOccurs > 1
https://github.com/tefra/xsdata/blob/6fba8384410088f11ff4accbd5b86e51b947d8fe/xsdata/codegen/handlers/attribute_compound_choice.py#L28-L34
The xsd is saying that
DeemedRateType2Choiceshould have one and only one child, and that child can be eitherDeemedRateType1CodeorGenericIdentification47. The generated dataclass doesn't doesn't include any of these restrictions or include any metadata that would allow me to enforce that restriction.
I get the point, it's not clear that only one of them can appear when you are building the object...
Thank you @dziegler
I believe there should either be Meta information on the Class Type, or possibly on any element that uses that type in order to allow validation. I also believe there should be a configuration option to explicitly create all choice fields (same functionality as turning OFF compound fields), but still keeping the Meta choice information. My use case is that I want development environments to be able to use IntelliSense to know what choices are available, and I need all fields to have the exact same name as the XSD, for validation purposes.
Here is another sample:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="BagOfBlocks" type="BagOfBlocksType">
<xs:annotation>
<xs:documentation>Comment describing your root element</xs:documentation>
</xs:annotation>
</xs:element>
<xs:complexType name="BagOfBlocksType">
<xs:sequence>
<xs:element name="Block" type="BlockType" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="BlockType">
<xs:choice>
<xs:element name="Cylinder" type="CylinderType"/>
<xs:element name="Box" type="BoxType"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="BoxType">
<xs:sequence>
<xs:element name="Length" type="DistanceType"/>
<xs:element name="Width" type="DistanceType"/>
<xs:element name="Height" type="DistanceType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CylinderType">
<xs:sequence>
<xs:element name="Diameter" type="DistanceType"/>
<xs:element name="Height" type="DistanceType"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="DistanceType">
<xs:annotation>
<xs:documentation>Units of Meters</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:double">
<xs:minInclusive value="0"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
The BagOfBlocksType has the Blocks element of BlockType, which is a choice. So the Choice Metadata should either be on the BagOfBlocks.Block element, or on the BlockType class.
This was fixed in #747