'Is there a way to deal with optionals of simple types that require a custom deserializer?

I have a struct which has its own deserialize method and optional DateComponents, which in turn, require their own deserialize method.

The struct currently looks like this:

public struct RepetitionPattern: XMLIndexerDeserializable {
    let duration: DateComponents?
    let interval: DateComponents?
    let stopAtDurationEnd: Bool?
    
    public init(duration: DateComponents? = nil, interval: DateComponents? = nil, stopAtDurationEnd: Bool? = nil) {
        self.duration = duration
        self.interval = interval
        self.stopAtDurationEnd = stopAtDurationEnd
    }
    
    public static func deserialize(_ element: XMLIndexer) throws -> Self {
        let duration: DateComponents = try element["Duration"].value()
        let interval: DateComponents = try element["Interval"].value()
        return try RepetitionPattern(
            duration: duration.isValidDate ? duration : nil,
            interval: interval.isValidDate ? interval : nil,
            stopAtDurationEnd: element["StopAtDurationEnd"].value()
        )
    }
}

And the deserialize method for the DateComponents type looks like:

extension DateComponents: XMLElementDeserializable {
    public static func deserialize(_ element: SWXMLHash.XMLElement) throws -> Self {
        guard !element.text.isEmpty else {
            return DateComponents()
        }
        
        // Use durationFrom8601String from Swift_ISO8601_DurationParser to parse
        // the ISO 8601 string.
        guard let validDateComponents = DateComponents.durationFrom8601String(element.text) else {
            throw XMLDeserializationError.typeConversionFailed(type: "DateComponent", element: element)
        }

        return validDateComponents
    }
    
    public func validate() throws {
        // Empty validate. Only necessary for custom validation logic after parsing.
    }
}

Is there a way to return an optional from the deserialize method? Or is there a better mechanism than the following in the struct's deserialize method for dealing with optionals?

    public static func deserialize(_ element: XMLIndexer) throws -> Self {
        let duration: DateComponents = try element["Duration"].value()
        let interval: DateComponents = try element["Interval"].value()
        return try RepetitionPattern(
            duration: duration.isValidDate ? duration : nil,
            interval: interval.isValidDate ? interval : nil,
            stopAtDurationEnd: element["StopAtDurationEnd"].value()
        )
    }

Ideally, I'd be able to do something like the following in deserialize for DateComponents, but deserialize doesn't appear to support an optional return type:

public static func deserialize(_ element: SWXMLHash.XMLElement) throws -> DateComponents? {
    guard !element.text.isEmpty else {
        return nil
    }
        
    // Use durationFrom8601String from Swift_ISO8601_DurationParser to parse
    // the ISO 8601 string.
    guard let validDateComponents = DateComponents.durationFrom8601String(element.text) else {
        throw XMLDeserializationError.typeConversionFailed(type: "DateComponent", element: element)
    }

    return validDateComponents
}

The Duration and Internal tags are not missing in the XML that I receive. Instead, they look like:

<d1p7:Repetition>
    <d1p7:Duration i:nil="true" />
    <d1p7:Interval i:nil="true" />
    <d1p7:StopAtDurationEnd>false</d1p7:StopAtDurationEnd>
</d1p7:Repetition>

Given the above, I also looked at doing the following for RepetitionPattern's deserialize method, but it sure is ugly:

public static func deserialize(_ element: XMLIndexer) throws -> Self {
        let hasDuration: Bool? = element["Duration"].value(ofAttribute: "i:nil")
        let hasInterval: Bool? = element["Interval"].value(ofAttribute: "i:nil")
        let hasStopAtDurationEnd: Bool? = element["StopAtDurationEnd"].value(ofAttribute: "i:nil")
        return RepetitionPattern(
            duration: hasDuration != nil ? nil : try element["Duration"].value(),
            interval: hasInterval != nil ? nil : try element["Interval"].value(),
            stopAtDurationEnd: hasStopAtDurationEnd != nil ? nil : try element["StopAtDurationEnd"].value()
        )
    }

Thoughts?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source