Serializing a python subclass in a choice
I have created a subclass for XmlDateTime, which explicitly includes zoneinfo, which I obtain from another part of the object hierarchy. I am not able to use offset, because at that point the date is unknown, and therefore day light saving is.
class XmlDateTimeZoned(XmlDateTime):
"""Extended XmlTime with explicit timezone."""
def __new__(cls, year, month, day, hour, minute, second, fractional_second=0, offset=None, zoneinfo=None):
instance = super().__new__(cls, year, month, day, hour, minute, second, fractional_second, offset)
instance.zoneinfo = zoneinfo
return instance
After deserialisation apply this to all places, and this works, even without converters. But when this is applied at a position of a choice, the Serializer is not happy.
xsdata.exceptions.SerializerError: XmlElements undefined choice: `from_operating_day_ref_or_from_date` for `<class 'netexio.dbaccess.XmlDateTimeZoned'>`
Stepping through the code it seems that xsdata finds the correct XmlVar by data type. There are some places effort is done to check by mro or string serialisation if the object can be matched anyway. I am using the 'slotted' dataclasses. So assigning a new attribute to XmlDateTime is not an option.
I tried below to overwrite the behavior but that does not work.
def convert_xml_datetime_zoned(value: XmlDateTimeZoned) -> XmlDateTime:
return XmlDateTime(value.year, value.month, value.day, value.hour, value.minute, value.second)
def convert_xml_time_zoned(value: XmlTimeZoned) -> XmlTime:
return XmlTime(value.year, value.month, value.day, value.hour, value.minute, value.second)
converter.register_converter(XmlDateTimeZoned, convert_xml_datetime_zoned)
converter.register_converter(XmlTimeZoned, convert_xml_time_zoned)
Any suggestions how to do this without first materialising the entire tree, replacing the instances back, and then serialising?
Checking subclasses in choices, is not an option, there are cases when choices include both parent, subclasses.
Can you provide a practical example, of what you are trying to accomplice, maybe we can include the zoneinfo in the XmlDateTime and XmlTime classes
What I already know what works is converting XmlDateTimezoned to XmlDateTime in convert_choice. What I am trying to figure out is what and where the ConverterFactory is normally called. My assumption is that if I want to make this patch generic, the only thing I should do is to plug in any specified convertion directly under convert_choice. If you say that the ConverterFactory has nothing to do with generic Python types and only with string serialisation / deserialisation then I think there might be en opportunity to have an true object conversion option that is going between classes when (de)serialising a value. I think this could also work within a MVC-framework where for example a widget is directly part of a document. An application would define an XML document as template, where at the position of a str, a QLabel is attached. Upon serialisation the converter takes the value f the QLabel and uses that in the document.
My use case is related to the datatype itself. I am receiving XML documents with timetables. A timetable typically has a departure time and multiple passing times when through stops. Those times as typically in local time respecting the timezone, it is not a date time. The availability - a date range - of a journey in a timetable is defined so that the timetable of one journey can be reused over multiple days. If people exchange timetables between timezones this introduces problems. A single journey going from one timezone to the other, considering the timezones share the same day-light-saving dates, could use the XmlTime offset, since they are always stable and relative towards eachother. But the offset cannot encode the actual timezone the journey is described for, because the offset itself is dependent on the calendar date. In the winter the offset is +0100 while in summer +0200. In this schema the availability is not yet processed when parsing the document, hence I am at that point unaware what the actual offset could be. Why I would I actually need to know this? I want to implement a conversion to UTC. For this to work correctly the availability should fall within either day light saving or not, for overlapping date ranges the journey should be duplicated to have a version with and without daylight saving. At that point I want to use the ZoneInfo to transfer de XmlTime to the correct "offset" towards UTC.
Ok so you are parsing datetimes with offsets and you want to convert them to UTC.
>>> from xsdata.models.datatype import XmlDateTime
>>> from zoneinfo import ZoneInfo
>>> dt = XmlDateTime(2002, 1, 1, 12, 1, 1, 0, 120)
>>> dt.to_datetime().astimezone(ZoneInfo("UTC"))
datetime.datetime(2002, 1, 1, 10, 1, 1, tzinfo=zoneinfo.ZoneInfo(key='UTC'))
>>> utc_dt = dt.to_datetime().astimezone(ZoneInfo("UTC"))
>>> XmlDateTime.from_datetime(utc_dt)
XmlDateTime(2002, 1, 1, 10, 1, 1, 0, 0)
>>>
I am struggling to understand @skinkie
You are right XmlDateTime was a poor example. XmlDateTime obviously does have the date with it and could directly be handled. XmlTime does not. So my other thing is XmlTimeZoned where ZoneInfo is also added.
My problem is that I want to store the zone information denormalised in the object, so that in a later conversion step I can take the ZoneInfo and convert XmlDateTime, XmlTime towards for example a UTC representation. Does that make sense?
You can still get the tzinfo from the XmlTime object like this
XmlTime(12, 1, 1, 0, 120).to_time().tzinfo
The problem is that 120, is dependent on if it is summer or wintertime. Hence I can store that it is +02:00, but after it, I cannot know when day light saving should be applied. If I instead of 120 would store Europe/Athens then I would know that it is UTC+2, but in about two weeks it will be UTC+3, hence XmlTime(12, 1, 1, 0, 180).
Ok what do you suggest to do?
I would love to have a generic option to subsitute classes in xsData. Hence the abillity to register a Class as being a facade of the class that would actually be required, similar to register_converter. I understand this could only be a 1:1 relationship, otherwise it cannot determine which type should be used. In this case it would allow me to override the XmlTime class. I don't think there is a good solution for xs:time, since it natively has the issues already.
Go for it, @skinkie. I’ll accept the contribution if you’re up for making it happen.