Answers:
如果您不介意数据移位,可以使用以下方法:
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
}
}
或可选变体
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] }
}
}
假设Foo
符合Codable
或确实Encodable
可以做到这一点。
let struct = Foo(a: 1, b: 2)
let dict = try struct.asDictionary()
let optionalDict = struct.dictionary
如果您想使用其他方法(init(any)
),请查看此Init对象,该对象符合带有字典/数组的Codable
下面是简单的实现方式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)
}
}
用法类似于JSONEncoder
/ JSONDecoder
…
let dictionary = try DictionaryEncoder().encode(object)
和
let object = try DictionaryDecoder().decode(Object.self, from: dictionary)
为了方便起见,我将所有内容都放入了回购中…… https://github.com/ashleymills/SwiftDictionaryCoding
我创建了一个名为CodableFirebase的库,其最初目的是将其与Firebase数据库一起使用,但实际上它满足了您的需要:它创建了一个字典或任何其他类型,就像在其中一样,JSONDecoder
但您无需在此处进行双重转换就像您在其他答案中所做的一样。因此,它看起来像:
import CodableFirebase
let model = Foo(a: 1, b: 2)
let dict = try! FirebaseEncoder().encode(model)
我不确定这是否是最好的方法,但您绝对可以执行以下操作:
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)
let dict = try JSONSerialization.jsonObject(with: try JSONEncoder().encode(struct), options: []) as? [String: Any]
没有内置的方法可以做到这一点。如上面的回答,如果您没有性能问题,则可以接受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)
}
}
您可以使用以下代码尝试:
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)
我在这里力图使示例更短。在生产代码中,您应该适当地处理错误。
在某些项目中,我使用了快速反射。但是请注意,嵌套的可编码对象也不会在那里映射。
let dict = Dictionary(uniqueKeysWithValues: Mirror(reflecting: foo).children.map{ ($0.label!, $0.value) })
我绝对认为,仅Codable
用于编码字典或从字典编码就具有一定的价值,而无意使用JSON / Plists /任何东西。有很多API可以给您返回字典,也可以期待字典,并且能够轻松地与Swift结构或对象互换它们而无需编写无尽的样板代码,这是很好的。
我一直在研究一些基于Foundation JSONEncoder.swift源代码的代码(该代码实际上在内部实现了字典编码/解码,但未导出)。
可以在这里找到代码:https : //github.com/elegantchaos/DictionaryCoding
它仍然很粗糙,但是我对其进行了扩展,例如,它可以在解码时使用默认值填充缺失值。
我将Swift项目中的PropertyListEncoder修改为DictionaryEncoder,只需将字典中的最终序列化删除为二进制格式即可。您可以自己做,也可以从这里获取我的代码
可以这样使用:
do {
let employeeDictionary: [String: Any] = try DictionaryEncoder().encode(employee)
} catch let error {
// handle error
}
我写了一个要点来解决这个问题(不使用Codable协议)。请注意,它不会类型检查任何值,并且不能对可编码的值进行递归处理。
class DictionaryEncoder {
var result: [String: Any]
init() {
result = [:]
}
func encode(_ encodable: DictionaryEncodable) -> [String: Any] {
encodable.encode(self)
return result
}
func encode<T, K>(_ value: T, key: K) where K: RawRepresentable, K.RawValue == String {
result[key.rawValue] = value
}
}
protocol DictionaryEncodable {
func encode(_ encoder: DictionaryEncoder)
}
在Codable中没有直接的方法可以做到这一点。您需要为您的结构实现可编码/可解码协议。例如,您可能需要编写如下内容
typealias EventDict = [String:Int]
struct Favorite {
var all:EventDict
init(all: EventDict = [:]) {
self.all = all
}
}
extension Favorite: Encodable {
struct FavoriteKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: FavoriteKey.self)
for eventId in all {
let nameKey = FavoriteKey(stringValue: eventId.key)!
try container.encode(eventId.value, forKey: nameKey)
}
}
}
extension Favorite: Decodable {
public init(from decoder: Decoder) throws {
var events = EventDict()
let container = try decoder.container(keyedBy: FavoriteKey.self)
for key in container.allKeys {
let fav = try container.decode(Int.self, forKey: key)
events[key.stringValue] = fav
}
self.init(all: events)
}
}
我在这里https://github.com/levantAJ/AnyCodable创建了一个pod,以方便解码和编码, [String: Any]
以及[Any]
pod 'DynamicCodable', '1.0'
而且您能够解码和编码[String: Any]
,[Any]
import DynamicCodable
struct YourObject: Codable {
var dict: [String: Any]
var array: [Any]
var optionalDict: [String: Any]?
var optionalArray: [Any]?
enum CodingKeys: String, CodingKey {
case dict
case array
case optionalDict
case optionalArray
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
dict = try values.decode([String: Any].self, forKey: .dict)
array = try values.decode([Any].self, forKey: .array)
optionalDict = try values.decodeIfPresent([String: Any].self, forKey: .optionalDict)
optionalArray = try values.decodeIfPresent([Any].self, forKey: .optionalArray)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(dict, forKey: .dict)
try container.encode(array, forKey: .array)
try container.encodeIfPresent(optionalDict, forKey: .optionalDict)
try container.encodeIfPresent(optionalArray, forKey: .optionalArray)
}
}
这是基于协议的解决方案:
protocol DictionaryEncodable {
func encode() throws -> Any
}
extension DictionaryEncodable where Self: Encodable {
func encode() throws -> Any {
let jsonData = try JSONEncoder().encode(self)
return try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
}
}
protocol DictionaryDecodable {
static func decode(_ dictionary: Any) throws -> Self
}
extension DictionaryDecodable where Self: Decodable {
static func decode(_ dictionary: Any) throws -> Self {
let jsonData = try JSONSerialization.data(withJSONObject: dictionary, options: [])
return try JSONDecoder().decode(Self.self, from: jsonData)
}
}
typealias DictionaryCodable = DictionaryEncodable & DictionaryDecodable
这是如何使用它:
class AClass: Codable, DictionaryCodable {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
struct AStruct: Codable, DictionaryEncodable, DictionaryDecodable {
var name: String
var age: Int
}
let aClass = AClass(name: "Max", age: 24)
if let dict = try? aClass.encode(), let theClass = try? AClass.decode(dict) {
print("Encoded dictionary: \n\(dict)\n\ndata from decoded dictionary: \"name: \(theClass.name), age: \(theClass.age)\"")
}
let aStruct = AStruct(name: "George", age: 30)
if let dict = try? aStruct.encode(), let theStruct = try? AStruct.decode(dict) {
print("Encoded dictionary: \n\(dict)\n\ndata from decoded dictionary: \"name: \(theStruct.name), age: \(theStruct.age)\"")
}
这是字典->对象。迅捷5。
extension Dictionary where Key == String, Value: Any {
func object<T: Decodable>() -> T? {
if let data = try? JSONSerialization.data(withJSONObject: self, options: []) {
return try? JSONDecoder().decode(T.self, from: data)
} else {
return nil
}
}
}