简体   繁体   English

Swift 通用可串行化协议

[英]Swift generic Serializable protocol

I noticed that I have a lot of repeated code for getting/sending JSON to my app's API, but the only thing that is different is what entity is being (de)serialized.我注意到我有很多重复的代码用于获取/发送 JSON 到我的应用程序的 API,但唯一不同的是正在(反)序列化的实体。 So I came up with the following design (for brevity only the GET method):所以我想出了以下设计(为简洁起见,只有 GET 方法):

HTTPClient.swift HTTPClient.swift

func getEntityJSON<T: JSONSerializable>(
    type: T.Type,
    url: String,
    completionHandler: @escaping (_ result: T?,
                                  _ headers: [String: String]?,
                                  _ statusCode: Int?,
                                  _ error: Error?) -> Void) {
    HttpClient.sharedInstance().getJSON(url, completionHandler: { (jsonData, headers, statusCode, error) in
        if let error = error {
            completionHandler(nil, headers, statusCode, error)
        } else {
            if let entityData = jsonData {
                completionHandler(T.fromJSON(data: entityData), headers, statusCode, nil)
            }
        }
    })
}

to intend to use it like this:打算像这样使用它:

HTTPClient.sharedInstance().getEntity(type: Translation, url: url) { (translation, _, _, _) in
    // do stuff with Translation instance
}

and here is the JSONSerializable protocol:这是JSONSerializable协议:

import Foundation
import SwiftyJSON


protocol Serializable: Codable {
    static func deserialize<T: Codable>(data: Data) -> T?
    func serialize() -> Data?
}

extension Serializable {
    static func deserialize<T: Decodable>(data: Data) -> T? {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601Full)
        return try? decoder.decode(T.self, from: data)
    }
    
    func serialize() -> Data? {
        let encoder = JSONEncoder()
        encoder.keyEncodingStrategy = .convertToSnakeCase
        encoder.dateEncodingStrategy = .formatted(DateFormatter.iso8601Full)
        return try? encoder.encode(self)
    }
}

protocol JSONSerializable: Serializable {
    func toJSON() -> JSON?
    static func fromJSON<T>(data: JSON) -> T?
}


extension JSONSerializable {
    func toJSON() -> JSON? {
        if let data = self.serialize() {
            return try? JSON(data: data)
        } else {
            return nil
        }
    }
}

This allows me to define a struct like this:这允许我定义一个这样的结构:

Translation.swift翻译.swift


struct Translation: Hashable, Identifiable, JSONSerializable {
    var id: UUID
    ...
    
    static func fromJSON(data: JSON) -> Translation? {

and I will get serialize, deserialize, toJSON functions.我将获得serialize, deserialize, toJSON函数。 But the compiler complains that Translation does not conform to JSONSerializable , due to lack of:但是编译器抱怨Translation不符合JSONSerializable ,因为缺少:

static func fromJSON<T>(data: JSON) -> T? . . I thought I would be able to implement that function with a concrete type, Translation in this case.我想我可以用一个具体的类型来实现 function,在这种情况下是Translation

I would like to be both able to do Translations.fromJSON(data: data) as well as T.fromJSON(data: data) .我希望能够同时执行Translations.fromJSON(data: data)T.fromJSON(data: data) How can I achieve this?我怎样才能做到这一点?

Generics ( <T> ) means that your method can work with any type that the caller (not the callee, not the implementer, not anyone else) specifies. Generics ( <T> ) 意味着您的方法可以使用调用者(不是被调用者,不是实现者,不是其他任何人)指定的任何类型。 In the case of fromJSON , this is clearly not the case.fromJSON的情况下,显然不是这样。 Any concrete implementation of fromJSON only works with T being the enclosing type, so fromJSON is not suitable to be generic. fromJSON的任何具体实现仅适用于T作为封闭类型,因此fromJSON不适合泛型。

When a method "only works with T being the enclosing type", it is the use case of Self types:当方法“仅适用于T作为封闭类型”时,它是Self类型的用例:

protocol JSONSerializable: Serializable {
    func toJSON() -> JSON?
    static func fromJSON(data: JSON) -> Self?
}

This way, your Translation implementation would conform to the protocol.这样,您的Translation实现将符合协议。

Similarly, deserialise should also be declared as:同样, deserialise也应该声明为:

static func deserialize(data: Data) -> Self?

If you actually want a JSONSerialisable that can turn JSON into any type that the caller wants, then it's less of a JSONSerialisable , and more of a JSONSerialiser , and it doesn't make much sense to inherit from Codable in that case.如果您确实想要一个JSONSerialisable可以将JSON转换为调用者想要的任何类型,那么它不是JSONSerialisable ,而是更多JSONSerialiser ,在这种情况下从Codable继承没有多大意义。

OK I ended up implementing a JSONSerializer , because I couldn't get it to work for collections of items to be (de)serialized.好的,我最终实现了JSONSerializer ,因为我无法让它为 collections 的要(反)序列化的项目工作。 This is how it currently looks:这是它目前的样子:

import Foundation
import SwiftyJSON


protocol JSONSerializable {
    static func fromJSON(data: JSON) -> Self?
}


class JSONSerializer {
    static func deserialize<T: Decodable>(_ type: T.Type, data: Data) -> T? {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601Full)
        return try? decoder.decode(type, from: data)
    }
    
    static func serialize<T: Encodable>(_ object: T) -> Data? {
        let encoder = JSONEncoder()
        encoder.keyEncodingStrategy = .convertToSnakeCase
        encoder.dateEncodingStrategy = .formatted(DateFormatter.iso8601Full)
        return try? encoder.encode(object)
    }
    
    static func fromJSON<T: JSONSerializable>(type: T.Type, data: JSON) -> T? {
        T.fromJSON(data: data)
    }
    
    static func toJSON<T: Encodable>(_ object: T) -> JSON? {
        if let data = Self.serialize(object) {
            return try? JSON(data: data)
        } else {
            return nil
        }
    }
}

I will look into the fromJSON function later to see if I can completely get rid of JSONSerializable and implemented it in the JSONSerializer like this:稍后我将研究fromJSON function 看看我是否可以完全摆脱JSONSerializable并在JSONSerializer中实现它,如下所示:

    static func fromJSON<T: Decodable>(type: T.Type, data: JSON) -> T? {
        if let jsonData = try? data.rawData() {
            return Self.deserialize(type, data: jsonData)
        }
        return nil
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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