2

我希望将模型对象存储在 Dictionary 中,并希望将整个字典序列JSONEncoder化为数据,然后将其序列化为字符串并保存。

这个想法是使用 Swift 4 开箱即用Encodable,以确保我添加到字典中的任何内容都将被序列化,其中可以包括原语和自定义对象(它们本身将符合Encodable)。

挑战是我应该将字典声明为什么类型:

  • 如果我使用[String: Any],它将不知道如何编码Any,并且如果我必须将其转换为实际的具体类型,这有点违背泛型的目的
  • 如果我使用[String: Encodable],它会在运行时崩溃,说 Encodable 不符合自身,这是可以理解的,因为它需要一个具体的类型

为了解决这个问题,我想到了创建一个包装器:即具有关联类型的协议具有泛型类型值的结构:

struct Serializable<T: Encodable> {
    var value: T?

    init(value: T) {
       self.value = value
    }
}

但是问题仍然存在,在声明上述字典的类型时,我仍然必须提供具体类型..

var dictionary: [String: Serializable<X>]

“X”应该在这里,或者,实现这一目标的正确方法是什么?我错过了什么?

4

2 回答 2

2

两种可能的方法:

  1. 您可以创建字典,其值是Encodable简单编码基础值的包装器类型:

    struct EncodableValue: Encodable {
        let value: Encodable
    
        func encode(to encoder: Encoder) throws {
            try value.encode(to: encoder)
        }
    }
    

    然后你可以这样做:

    let dictionary = [
        "foo": EncodableValue(value: Foo(string: "Hello, world!")),
        "bar": EncodableValue(value: Bar(value: 42)),
        "baz": EncodableValue(value: "qux")
    ]
    
    let data = try! JSONEncoder().encode(dictionary)
    
  2. 您可以定义自己的Codable类型而不是使用字典:

    struct RequestObject: Encodable {
        let foo: Foo
        let bar: Bar
        let baz: String
    }
    
    let requestObject = RequestObject(
        foo: Foo(string: "Hello, world!"), 
        bar: Bar(value: 42),
        baz: "qux"
    )
    
    let data = try! JSONEncoder().encode(requestObject)
    

不用说,这些都假定两者FooBar符合Encodable.

于 2018-01-20T17:18:56.740 回答
0

这是我的解决方案(由 Rob 回答改进):

struct EncodableValue: Encodable {
    let value: Encodable

    func encode(to encoder: Encoder) throws {
        try value.encode(to: encoder)
    }
}

struct Storage: Encodable {
    var dict: [String: Encodable] = [:]
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        for (key, value) in dict {
            guard let codingKey = CodingKeys(stringValue: key) else {
                continue
            }
            if let enc = value as? EncodableValue {
                try container.encode(enc, forKey: codingKey)
            }
        }
    }

    struct CodingKeys: CodingKey {
        var stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        var intValue: Int?
        init?(intValue: Int) {
            return nil
        }
    }
}

let dict: [String: EncodableValue] = ["test": EncodableValue(value:1), "abc":EncodableValue(value:"GOGO")]
let storage = Storage(dict: dict)

do {
    let data = try JSONEncoder().encode(storage)
    let res = String(data: data, encoding: .utf8)
    print(res ?? "nil")
} catch {
    print(error)
}
于 2018-01-21T12:08:48.170 回答