Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to choose an element of a choice having the same target enumeration? #913

Closed
skinkie opened this issue Jan 8, 2024 · 10 comments
Closed

Comments

@skinkie
Copy link
Contributor

skinkie commented Jan 8, 2024

Very pretty example of something that xsData can't handle, or can it?

                        <xsd:choice>
                                <xsd:element name="FuelType" type="FuelTypeEnumeration" minOccurs="0">
                                        <xsd:annotation>
                                                <xsd:documentation>The type of fuel used by a vehicle of the type. +1.2.2</xsd:documentation>
                                        </xsd:annotation>
                                </xsd:element>
                                <xsd:element name="TypeOfFuel" type="FuelTypeEnumeration" minOccurs="0">
                                        <xsd:annotation>
                                                <xsd:documentation>The type of fuel used by a vehicle of the type. DEPRECATED NAME  1.2.2</xsd:documentation>
                                        </xsd:annotation>
                                </xsd:element>
                        </xsd:choice>
    fuel_type_or_type_of_fuel: Optional[FuelTypeEnumeration] = field(
        default=None,
        metadata={
            "type": "Elements",
            "choices": (
                {
                    "name": "FuelType",
                    "type": FuelTypeEnumeration,
                    "namespace": "http://www.netex.org.uk/netex",
                },
                {
                    "name": "TypeOfFuel",
                    "type": FuelTypeEnumeration,
                    "namespace": "http://www.netex.org.uk/netex",
                },
            ),
        },
    )
@skinkie skinkie changed the title How to choose an element of a choice having the same target type? How to choose an element of a choice having the same target enumeration? Jan 8, 2024
@tefra
Copy link
Owner

tefra commented Jan 8, 2024

It does sort of, kinda :) in these cases where it's impossible to derive the element because the value type is ambiguous, the parser is using derived elements

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="root">
        <xsd:complexType>
            <xsd:choice maxOccurs="unbounded">
                <xsd:element name="FuelType" type="FuelTypeEnumeration"/>
                <xsd:element name="TypeOfFuel" type="FuelTypeEnumeration"/>
            </xsd:choice>
        </xsd:complexType>
    </xsd:element>
    <xsd:simpleType name="FuelTypeEnumeration">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="a"/>
            <xsd:enumeration value="b"/>
        </xsd:restriction>
    </xsd:simpleType>
</xsd:schema>
xml = """
<root>
<FuelType>a</FuelType>
<TypeOfFuel>b</TypeOfFuel>
</root>

"""
parser = XmlParser()
res = parser.from_string(xml)
print(res)
>>> Root(fuel_type_or_type_of_fuel=[<FuelTypeEnumeration.A: 'a'>, DerivedElement(qname='TypeOfFuel', value=<FuelTypeEnumeration.B: 'b'>, type=None)])

@tefra
Copy link
Owner

tefra commented Jan 8, 2024

Of course serialization with the derived element wrapper works as expected

@skinkie
Copy link
Contributor Author

skinkie commented Jan 8, 2024

So for serialisation you would need to use DerivedElement?

@tefra
Copy link
Owner

tefra commented Jan 8, 2024

Yes

@skinkie
Copy link
Contributor Author

skinkie commented Jan 9, 2024

@tefra considering the one below. The choice has different target types, but the only way to differentiate between is using the type but not anything related to an element name - or so it seems.

image

@dataclass(kw_only=True)
class AboAnfrageType:
    choice: List[
        Union[
            AboAsbrefType,
            AboAsbtype,
            AboAzbrefType,
            AboAzbtype,
            AboVistype,
            AboAndtype,
            AboAusrefType,
            AboAustype,
            int,
            bool,
        ]
    ] = field(
        default_factory=list,
        metadata={
            "type": "Elements",
            "choices": (
                {
                    "name": "AboASBRef",
                    "type": AboAsbrefType,
                    "namespace": "",
                },
                {
                    "name": "AboASB",
                    "type": AboAsbtype,
                    "namespace": "",
                },
                {
                    "name": "AboAZBRef",
                    "type": AboAzbrefType,
                    "namespace": "",
                },
                {
                    "name": "AboAZB",
                    "type": AboAzbtype,
                    "namespace": "",
                },
                {
                    "name": "AboVIS",
                    "type": AboVistype,
                    "namespace": "",
                },
                {
                    "name": "AboAND",
                    "type": AboAndtype,
                    "namespace": "",
                },
                {
                    "name": "AboAUSRef",
                    "type": AboAusrefType,
                    "namespace": "",
                },
                {
                    "name": "AboAUS",
                    "type": AboAustype,
                    "namespace": "",
                },
                {
                    "name": "AboLoeschen",
                    "type": int,
                    "namespace": "",
                },
                {
                    "name": "AboLoeschenAlle",
                    "type": bool,
                    "namespace": "",
                },
            ),
        },
    )
    sender: str = field(
        metadata={
            "name": "Sender",
            "type": "Attribute",
            "required": True,
        }
    )
    zst: XmlDateTime = field(
        metadata={
            "name": "Zst",
            "type": "Attribute",
            "required": True,
        }
    )

@tefra
Copy link
Owner

tefra commented Jan 10, 2024

I am not sure I follow @skinkie

@skinkie
Copy link
Contributor Author

skinkie commented Jan 10, 2024

I am not sure I follow @skinkie

Consider the schema below. xsData gives me a List of different types, int or boolean for the choice element. If I want to know if AboLoeschenAlle is sent, the only option that I have is to check for bool, not the actual element name.

            if isinstance(abo_anfrage_type.choice, bool):
                await abo_loeschen_alle(abo_anfrage_type.sender)
                antwort = AboAntwortType(bestaetigung=BestaetigungType(fehlernummer="0", ergebnis=ErgebnisType.OK, zst=XmlDateTime.now()))

            elif isinstance(abo_anfrage_type.choice, int):
                await abo_loeschen(abo_anfrage_type.sender, abo_anfrage_type.choice)
                antwort = AboAntwortType(bestaetigung=BestaetigungType(fehlernummer="0", ergebnis=ErgebnisType.OK, zst=XmlDateTime.now()))

            elif isinstance(abo_anfrage_type.choice, list):
                abo_aus_types = [abo_aus_type for abo_aus_type in abo_anfrage_type.choice if isinstance(abo_aus_type, AboAustype)]
                for abo_aus_type in abo_aus_types:
                    await abo_aus(abo_anfrage_type.sender, abo_aus_type)
        <xsd:element name="AboAnfrage" type="AboAnfrageType"/>
        <xsd:complexType name="AboAnfrageType">
                <xsd:choice>
                        <xsd:group ref="AboGroup"/>
                        <xsd:choice>
                                <xsd:element name="AboLoeschen" type="AboIDType" minOccurs="0" maxOccurs="unbounded"/>
                                <xsd:element name="AboLoeschenAlle" type="xsd:boolean" minOccurs="0"/>
                        </xsd:choice>
                </xsd:choice>
                <xsd:attribute name="Sender" type="SenderType" use="required"/>
                <xsd:attribute name="Zst" type="xsd:dateTime" use="required"/>
        </xsd:complexType>
        <xsd:group name="AboGroup">
                <xsd:choice>
                        <xsd:element name="AboASBRef" type="AboASBRefType" minOccurs="0" maxOccurs="unbounded"/>
                        <xsd:element name="AboASB" type="AboASBType" minOccurs="0" maxOccurs="unbounded"/>
                        <xsd:element name="AboAZBRef" type="AboAZBRefType" minOccurs="0" maxOccurs="unbounded"/>
                        <xsd:element name="AboAZB" type="AboAZBType" minOccurs="0" maxOccurs="unbounded"/>
                        <xsd:element name="AboVIS" type="AboVISType" minOccurs="0" maxOccurs="unbounded"/>
                        <xsd:element name="AboAND" type="AboANDType" minOccurs="0" maxOccurs="unbounded"/>
                        <xsd:element name="AboAUSRef" type="AboAUSRefType" minOccurs="0" maxOccurs="unbounded"/>
                        <xsd:element name="AboAUS" type="AboAUSType" minOccurs="0" maxOccurs="unbounded"/>
                </xsd:choice>
        </xsd:group>

@tefra
Copy link
Owner

tefra commented Jan 11, 2024

That's the case of all union dataclasses fields, you have to check the instance of the value before you process it.
Would you prefer to wrap all values with a DerivedElement?

@skinkie
Copy link
Contributor Author

skinkie commented Jan 11, 2024

I don't know what the best option would be. Obviously it could be convinient to have something like choice.AboASBRef, but I don't know how feasible such interface would be especially with an ordered list.

@tefra
Copy link
Owner

tefra commented Jan 20, 2024

Let's continue here #920, easier examples...

@tefra tefra closed this as completed Jan 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants