Swift - 解码/编码具有不同类型的泛型数组



如何解码/编码不同泛型类型的数组?

我有一个数据结构,它具有符合协议Connection的属性,因此我使用泛型:

// Data structure which saves two objects, which conform to the Connection protocol
struct Configuration<F: Connection, T: Connection>: Codable {
var from: F
var to: T
private var id: String = UUID.init().uuidString
enum CodingKeys: String, CodingKey {
case from, to, id
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.from = try container.decode(F.self, forKey: .from)
self.to = try container.decode(T.self, forKey: .to)
self.id = try container.decode(String.self, forKey: .id)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(from, forKey: .from)
try container.encode(to, forKey: .to)
try container.encode(id, forKey: .id)
}
}
protocol Connection: Codable {
var path: String { get set }
}

// Two implementations of the Connection protocol
struct SFTPConnection: Connection, Codable {
var path: String
var user: String
var sshKey: String
}
struct FTPConnection: Connection, Codable {
var path: String
var user: String
var password: String
}

当我知道FT的连接类型时,这工作正常。但是我在某些情况下,我想加载配置,不知道FT是哪种类型。

public static func load<F: Connection, T: Connection>(for key: String) throws -> Configuration<F, T>? {
// Load from UserDefaults
guard let configurationData = defaults.object(forKey: key) as? Data else {
return nil
}
// Decode
guard let configuration = try? PropertyListDecoder().decode(Configuration<F, T>.self, from: configurationData) else {
return nil
}
return configuration
}
// OR
func loadAll<F:Connection, T: Connection>() -> [String: Configuration<F, T>]? {
return UserDefaults.standard.dictionaryRepresentation() as? [String: Configuration<F, T>]
}

在上述情况下,FT可以是任何符合Connection协议的未知类型。所以上面的函数不起作用,因为我在调用函数时需要为FT指定特定的类型,我不知道。

在第二个函数中,F实际上可以属于不同的类型。这就是它变得困难的地方。我想我需要以某种方式将FT的类型也存储在用户默认值中,然后在decodeencode函数中使用它们(从而丢弃泛型(。但我不知道我该如何优雅地做到这一点。

我将不胜感激有关如何解决此问题的任何想法!

以下解决方案解决了我在泛型方面遇到的所有问题,并且不知道Connection的具体类型。解决方案的关键是

  1. 在实现本身中保存Connection实现的类型,以及
  2. 使用superEncodersuperDecoderfromto属性进行编码/解码。

这是解决方案:

import Foundation
protocol Connection: Codable {
var type: ConnectionType { get }
var path: String { get set }
}

struct LocalConnection: Connection {
let type: ConnectionType = ConnectionType.local
var path: String
}

struct SFTPConnection : Connection {
let type: ConnectionType = ConnectionType.sftp
var path: String
var user: String
var sshKey: String
init(path: String, user: String, sshKey: String) {
self.path = path
self.user = user
self.sshKey = sshKey
}
}

struct FTPConnection: Connection {
let type: ConnectionType = ConnectionType.ftp
var path: String
var user: String
var password: String
}

struct TFTPConnection: Connection {
let type: ConnectionType = ConnectionType.tftp
var path: String
}


enum ConnectionType : Int, Codable {
case local
case sftp
case ftp
case tftp
func getType() -> Connection.Type {
switch self {
case .local: return LocalConnection.self
case .sftp: return SFTPConnection.self
case .ftp: return FTPConnection.self
case .tftp: return TFTPConnection.self
}
}
}


struct Configuration {
var from : Connection
var to : Connection
private var id = UUID.init().uuidString
var fromType : ConnectionType { return from.type }
var toType : ConnectionType { return to.type }
init(from: Connection, to: Connection) {
self.from = from
self.to = to
}
}

extension Configuration : Codable {
enum CodingKeys: String, CodingKey {
case id, from, to, fromType, toType
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
var type : ConnectionType
type = try container.decode(ConnectionType.self, forKey: .fromType)
let fromDecoder = try container.superDecoder(forKey: .from)
self.from = try type.getType().init(from: fromDecoder)
type = try container.decode(ConnectionType.self, forKey: .toType)
let toDecoder = try container.superDecoder(forKey: .to)
self.to = try type.getType().init(from: toDecoder)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: .id)
try container.encode(self.fromType, forKey: .fromType)
let fromContainer = container.superEncoder(forKey: .from)
try from.encode(to: fromContainer)
try container.encode(self.toType, forKey: .toType)
let toContainer = container.superEncoder(forKey: .to)
try to.encode(to: toContainer)
}
}

相关内容

  • 没有找到相关文章

最新更新