如何使用Swift的Codable编码成字典?

zou*_*oul 93 swift swift4 codable

我有一个实现Swift 4的结构Codable.是否有一种简单的内置方法将该结构编码为字典?

let struct = Foo(a: 1, b: 2)
let dict = something(struct)
// now dict is ["a": 1, "b": 2]
Run Code Online (Sandbox Code Playgroud)

Chr*_*ore 183

如果你不介意周围的数据转移,你可以使用这样的东西:

extension Encodable {
  func asDictionary() throws -> [String: Any] {
    let data = try JSONEncoder().encode(self)
    guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
      throw NSError()
    }
    return dictionary
  }
}
Run Code Online (Sandbox Code Playgroud)

或者是可选的变体

extension Encodable {
  var dictionary: [String: Any]? {
    guard let data = try? JSONEncoder().encode(self) else { return nil }
    return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
  }
}
Run Code Online (Sandbox Code Playgroud)

假设Foo符合Codable或真的Encodable那么你可以做到这一点.

let struct = Foo(a: 1, b: 2)
let dict = try struct.asDictionary()
let optionalDict = struct.dictionary
Run Code Online (Sandbox Code Playgroud)

如果你想走另一条路(init(any)),看看这个Init符合Codable的对象和字典/数组

  • 编码到数据中,然后从数据中解码,当解码大块数据时,对性能的惩罚一定是明显的。 (3认同)

Ash*_*lls 17

以下是DictionaryEncoder/ DictionaryDecoder包装的简单实现JSONEncoder,JSONDecoder并且JSONSerialization还处理编码/解码策略......

class DictionaryEncoder {

    private let encoder = JSONEncoder()

    var dateEncodingStrategy: JSONEncoder.DateEncodingStrategy {
        set { encoder.dateEncodingStrategy = newValue }
        get { return encoder.dateEncodingStrategy }
    }

    var dataEncodingStrategy: JSONEncoder.DataEncodingStrategy {
        set { encoder.dataEncodingStrategy = newValue }
        get { return encoder.dataEncodingStrategy }
    }

    var nonConformingFloatEncodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy {
        set { encoder.nonConformingFloatEncodingStrategy = newValue }
        get { return encoder.nonConformingFloatEncodingStrategy }
    }

    var keyEncodingStrategy: JSONEncoder.KeyEncodingStrategy {
        set { encoder.keyEncodingStrategy = newValue }
        get { return encoder.keyEncodingStrategy }
    }

    func encode<T>(_ value: T) throws -> [String: Any] where T : Encodable {
        let data = try encoder.encode(value)
        return try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
    }
}

class DictionaryDecoder {

    private let decoder = JSONDecoder()

    var dateDecodingStrategy: JSONDecoder.DateDecodingStrategy {
        set { decoder.dateDecodingStrategy = newValue }
        get { return decoder.dateDecodingStrategy }
    }

    var dataDecodingStrategy: JSONDecoder.DataDecodingStrategy {
        set { decoder.dataDecodingStrategy = newValue }
        get { return decoder.dataDecodingStrategy }
    }

    var nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy {
        set { decoder.nonConformingFloatDecodingStrategy = newValue }
        get { return decoder.nonConformingFloatDecodingStrategy }
    }

    var keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy {
        set { decoder.keyDecodingStrategy = newValue }
        get { return decoder.keyDecodingStrategy }
    }

    func decode<T>(_ type: T.Type, from dictionary: [String: Any]) throws -> T where T : Decodable {
        let data = try JSONSerialization.data(withJSONObject: dictionary, options: [])
        return try decoder.decode(type, from: data)
    }
}
Run Code Online (Sandbox Code Playgroud)

用法类似于JSONEncoder/ JSONDecoder...

let dictionary = try DictionaryEncoder().encode(object)
Run Code Online (Sandbox Code Playgroud)

let object = try DictionaryDecoder().decode(Object.self, from: dictionary)
Run Code Online (Sandbox Code Playgroud)

为方便起见,我把这一切都放在了回购中......  https://github.com/ashleymills/SwiftDictionaryCoding


Noo*_*ass 16

我创建了一个名为CodableFirebase的库,它最初的目的是将它与Firebase数据库一起使用,但它实际上是你所需要的:它创建一个字典或任何其他类型,JSONDecoder但你不需要在这里进行双重转换就像你在其他答案中所做的那样 所以它看起来像:

import CodableFirebase

let model = Foo(a: 1, b: 2)
let dict = try! FirebaseEncoder().encode(model)
Run Code Online (Sandbox Code Playgroud)


Law*_*iet 7

我不确定这是否是最好的方式,但你绝对可以做到这样的事情:

struct Foo: Codable {
    var a: Int
    var b: Int

    init(a: Int, b: Int) {
        self.a = a
        self.b = b
    }
}

let foo = Foo(a: 1, b: 2)
let dict = try JSONDecoder().decode([String: Int].self, from: JSONEncoder().encode(foo))
print(dict)
Run Code Online (Sandbox Code Playgroud)

  • 这仅适用于具有相同类型的所有属性的结构 (5认同)

小智 6

let dict = try JSONSerialization.jsonObject(with: try JSONEncoder().encode(struct), options: []) as? [String: Any]


Mar*_*moy 5

我已将Swift 项目中的PropertyListEncoder修改为 DictionaryEncoder,只需将最终序列化从字典删除为二进制格式即可。您可以自己做同样的事情,或者您可以从这里获取我的代码

它可以这样使用:

do {
    let employeeDictionary: [String: Any] = try DictionaryEncoder().encode(employee)
} catch let error {
    // handle error
}
Run Code Online (Sandbox Code Playgroud)


小智 5

在一些项目中,我使用了快速反射。但要小心,嵌套的可编码对象也不会映射到那里。

let dict = Dictionary(uniqueKeysWithValues: Mirror(reflecting: foo).children.map{ ($0.label!, $0.value) })
Run Code Online (Sandbox Code Playgroud)


小智 5

没有内置的方法可以做到这一点。如上面的回答,如果您没有性能问题,则可以接受JSONEncoder+ JSONSerialization实现。

但是我宁愿使用标准库提供编码器/解码器对象的方式。

class DictionaryEncoder {
    private let jsonEncoder = JSONEncoder()

    /// Encodes given Encodable value into an array or dictionary
    func encode<T>(_ value: T) throws -> Any where T: Encodable {
        let jsonData = try jsonEncoder.encode(value)
        return try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
    }
}

class DictionaryDecoder {
    private let jsonDecoder = JSONDecoder()

    /// Decodes given Decodable type from given array or dictionary
    func decode<T>(_ type: T.Type, from json: Any) throws -> T where T: Decodable {
        let jsonData = try JSONSerialization.data(withJSONObject: json, options: [])
        return try jsonDecoder.decode(type, from: jsonData)
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用以下代码进行尝试:

struct Computer: Codable {
    var owner: String?
    var cpuCores: Int
    var ram: Double
}

let computer = Computer(owner: "5keeve", cpuCores: 8, ram: 4)
let dictionary = try! DictionaryEncoder().encode(computer)
let decodedComputer = try! DictionaryDecoder().decode(Computer.self, from: dictionary)
Run Code Online (Sandbox Code Playgroud)

我在这里力图使示例更短。在生产代码中,您应该适当地处理错误。