简体   繁体   中英

Deserialising a class with SWXMLHash in Swift 3

I'm trying to deserialise XML to a class using the example provided at the end of the readme, but it's raising the same compile time error as originally provoked this question .

method 'deserialize' in non-final class 'Element' must return Self to conform to protocol 'XMLElementDeserializable'

I have tried to use the example as verbatim as possible (although changing quite a bit as Date, a struct, is now mainly used over NSDate), but I am still getting the same issue.

This is the code I'm trying to use (essentially identical just stripped down for clarity

import Foundation
import SWXMLHash

class Element: XMLElementDeserializable {

    public static func deserialize(element: SWXMLHash.XMLElement) throws -> Self {
        return value(Element())
    }

    private static func value<T>(_ element: Element) -> T {
        return element as! T
    }

}

deserialize implementation does not implement the one which is blueprinted

Your own deserialize function does not implement the one blueprinted from XMLElementDeserializable , meaning a default implementation of deserialize (from XMLElementDeserializable ) will become available to your class Element . This default implementation is a throwing erroneous one, which is the source of your somewhat obfuscated error message.

From the source code of the SWXMLHash framework

 /// Provides XMLElement deserialization / type transformation support public protocol XMLElementDeserializable { /// Method for deserializing elements from XMLElement static func deserialize(_ element: XMLElement) throws -> Self /* ^^^^^^^^^- take note of the omitted external name in this signature */ } /// Provides XMLElement deserialization / type transformation support public extension XMLElementDeserializable { /** A default implementation that will throw an error if it is called - parameters: - element: the XMLElement to be deserialized - throws: an XMLDeserializationError.ImplementationIsMissing if no implementation is found - returns: this won't ever return because of the error being thrown */ static func deserialize(_ element: XMLElement) throws -> Self { throw XMLDeserializationError.ImplementationIsMissing( method: "XMLElementDeserializable.deserialize(element: XMLElement)") } } 

Note the mismatch between the signature of your deserialize method and the blueprinted one: the latter explicitly omits its external parameter name ( _ ), whereas yours does not.

Analyzing the error case with a minimal example

We can construct an analogous, minimal example to achieve the same error message.

enum FooError : Error {
    case error
}

protocol Foo {
    static func bar(_ baz: Int) throws -> Self
}

extension Foo {
    static func bar(_ baz: Int) throws -> Self {
        throw FooError.error
    }
}

// Bar implements its own bar(baz:) method, one which does
// NOT implement bar(_:) from the protocol Foo. This means
// that the throwing erroneous default implementation of 
// bar(_:) becomes available to Bar, yielding the same error
// message as in your question
class Bar : Foo {
    // does not match the blueprint!
    static func bar(baz: Int) throws -> Self {
        return value(Bar())
    }

    private static func value<T>(_ bar: Bar) -> T {
        return bar as! T
    }
}

     // Error!

Which yields the following error message:

error: method ' bar ' in non-final class ' Bar ' must return Self to conform to protocol ' Foo '

  static func bar(_ baz: Int) throws -> Self { ... 

If we fix the signature of the bar method in Bar to match that of the one blueprinted in Foo , we're no longer prompted with an error

/* ... FooError and Foo as above */

// Bar implements bar(_:) from protocol Foo, which
// means the throwing erroneous default implementation
// of bar(_:) is never in effect, OK
class Bar : Foo {
    static func bar(_ baz: Int) throws -> Self {
        return value(Bar())
    }

    private static func value<T>(_ bar: Bar) -> T {
        return bar as! T
    }
}

To avoid the type inference fix (inferring Bar() as a valid Self instance), mark Bar as final and explicitly annotate the type of Self

/* ... FooError and Foo as above */

final class Bar : Foo {
    static func bar(_ baz: Int) throws -> Bar {
        return Bar()
    }
}

Applied to your use case

Taken the above into account, you need to modify your deserialize signature into

public static func deserialize(_ element: SWXMLHash.XMLElement) throws -> Self { /* ... */ }

Or, in case you do not plan on subclassing the Element class itself, marking it final allowing annotating a concrete return type Element rather than Self :

final class Element: XMLElementDeserializable {
    public static func deserialize(_ element: SWXMLHash.XMLElement) throws -> Element { /* ... */ }
}

(Note that your current implementation of deserialized does not make much sense, as it does not make use of the object to be deserialized (internal parameter name element ), but since you mention your example is stripped down, I'll presume this is intended for the example).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM