-
-
Notifications
You must be signed in to change notification settings - Fork 61
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
Compound Fields only for Iterables #1070
Comments
I am not sure I follow @skinkie, you are suggesting to skip the compound field if there is no array element in the choices right? The example you give is not very accurate. This was the default behavior, but the counter-argument is that but having flat fields there is no hint that one of the fields must be present #737 If I am way off please provide a simple xsd example, not netex 😄 |
What I want to achieve is that unless it is a list of choices, opposed to a choice of lists, individual field access is possible. I even agree that the bug/behavior reported by #737 is what I intend for everything that is not a list. None of the examples in #737 are actually an ordered list of elements where the elements in the list may be a choice. Going to give an NeTEx example anyway ;-) I can simplyfy this, if we need a test. So the choice itself here is the element that is maxOccurs > 1. From my perspective that is the only time I care about order.
@martinwiesmann is not wrong with the statement "This class definition does not include any hint that only one of these elements are allowed." I think fundamentally that was what #1065 about, the choice is there, there can be only one value be present, but I would like to see that individual field names remain available. From my perspective the compound fields implementation is mixing two behaviors.
I am not disputing 1 and 2 should be done, 1 gives me something in practice is very difficult to write code for. The generation results in always changing code if anything ot the choice changes and replacing the choice attribute name with Your suggestion was to disable compound fields. An other approach where choices in category 2 stay as-is, but for choices in category 1 something like below is made available. block_ref exist because it originates from a substitution group, this will obviously cause issues so maybe we need to revert that back to block_ref_or_train_block_ref. @dataclass(kw_only=True)
class ServiceJourneyVersionStructure(JourneyVersionStructure):
class Meta:
name = "ServiceJourney_VersionStructure"
block_ref: Optional[Union[TrainBlockRef, BlockRef]] = field(
default=None,
metadata={
"type": "Elements",
"choices": (
{
"name": "TrainBlockRef",
"type": TrainBlockRef,
"namespace": "http://www.netex.org.uk/netex",
},
{
"name": "BlockRef",
"type": BlockRef,
"namespace": "http://www.netex.org.uk/netex",
},
),
},
)
operator_ref_or_operator_view: Optional[Union[OperatorRef, OperatorView]] = field(
default=None,
metadata={
"type": "Elements",
"choices": (
{
"name": "OperatorRef",
"type": OperatorRef,
"namespace": "http://www.netex.org.uk/netex",
},
{
"name": "OperatorView",
"type": OperatorView,
"namespace": "http://www.netex.org.uk/netex",
},
),
},
)
@property
def operator_ref(self) -> OperatorRef:
if isinstance(self.operator_ref_or_operator_view, OperatorRef):
return self.operator_ref_or_operator_view
@operator_ref.setter
def operator_ref(self, v: OperatorRef) -> None:
self.operator_ref_or_operator_view = v
@property
def operator_view(self) -> OperatorView:
if isinstance(self.operator_ref_or_operator_view, OperatorView):
return self.operator_ref_or_operator_view
@operator_view.setter
def operator_view(self, v: OperatorView) -> None:
self.operator_ref_or_operator_view = v The introduced properties do not seem to interfere with the actual XML generation. from xsdata.formats.dataclass.serializers import XmlSerializer
from xsdata.formats.dataclass.serializers.config import SerializerConfig
from netex import ServiceJourneyVersionStructure, OperatorRef, OperatorView
x = ServiceJourneyVersionStructure(id="test", version="1")
x.operator_ref = OperatorRef(ref="x", version="1")
print(f"x.operator_ref {x.operator_ref}")
print(f"x.operator_view {x.operator_view}")
print(f"x.operator_ref_or_operator_view {x.operator_ref_or_operator_view}")
x.operator_view = OperatorView(id="view")
print(f"x.operator_ref {x.operator_ref}")
print(f"x.operator_view {x.operator_view}")
print(f"x.operator_ref_or_operator_view {x.operator_ref_or_operator_view}")
serializer_config = SerializerConfig(ignore_default_attributes=True, pretty_print=True)
serializer_config.ignore_default_attributes = True
serializer = XmlSerializer(config=serializer_config)
print(serializer.render(x))
I would call this stable compound field access. I hope that my aim is clear. |
Thanks for giving a simple example not from netex |
This was from NeTEx, do you want me to copy paste an XML schema that is at most 40 lines? |
Yeah simple examples @skinkie I don't have the time/motivation to go through netex |
@tefra simple example: <?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.netex.org.uk/netex" xmlns:netex="http://www.netex.org.uk/netex" xmlns:siri="http://www.siri.org.uk/siri" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:core="http://www.govtalk.gov.uk/core" targetNamespace="http://www.netex.org.uk/netex" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0">
<xsd:element name="Id" abstract="false">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:integer" use="optional">
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="Ref" abstract="false">
<xsd:complexType>
<xsd:attribute name="ref" type="xsd:integer" use="optional">
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="CompoundTest" maxOccurs="1">
<xsd:complexType>
<xsd:choice minOccurs="1">
<xsd:element ref="Id" maxOccurs="1"/>
<xsd:element ref="Ref" maxOccurs="1"/>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema> Using the referenced branch creates individual access to compound properties guarding:
from dataclasses import dataclass, field
from typing import Optional, Union
from .id import Id
from .ref import Ref
__NAMESPACE__ = "http://www.netex.org.uk/netex"
@dataclass(kw_only=True)
class CompoundTest:
class Meta:
namespace = "http://www.netex.org.uk/netex"
id_or_ref: Optional[Union[Id, Ref]] = field(
default=None,
metadata={
"type": "Elements",
"choices": (
{
"name": "Id",
"type": Id,
},
{
"name": "Ref",
"type": Ref,
},
),
},
)
@property
def id(self):
if isinstance(self.id_or_ref, Id):
return self.id_or_ref
@id.setter
def id(self, value) -> None:
self.id_or_ref = value
@property
def ref(self):
if isinstance(self.id_or_ref, Ref):
return self.id_or_ref
@ref.setter
def ref(self, value) -> None:
self.id_or_ref = value |
A follow up on #1065.
CompoundFields currently aggregate choices, which allows it to render a choice item in the found order. In the regular case where an object has two fields, and either one of those fields must be set (or read) I would prefer to access that field by its field name, not
field1_field2
orchoice
. There is only one situation - I am aware of - where I want to maintain order: this is when the choice field is part of a sequence with maxOccurs > 1, hence it becomes a List.To make it very concrete, first is with compound fields, the second is not. I wish that if and only if default_factory is a list, the compound code would be applied. I'll obviously give it a shot.
The text was updated successfully, but these errors were encountered: