【问题标题】:Init an object conforming to Codable with a dictionary/array使用字典/数组初始化符合 Codable 的对象
【发布时间】:2018-03-01 19:47:28
【问题描述】:

我的主要用例是使用字典创建一个对象:例如

struct Person: Codable { let name: String }    
let dictionary = ["name": "Bob"]
let person = Person(from: dictionary)    

我想避免编写自定义实现,并希望尽可能高效。

【问题讨论】:

    标签: swift codable


    【解决方案1】:

    目前我最好的解决方案是这个,但它有编码/解码的开销。

    extension Decodable {
      init(from: Any) throws {
        let data = try JSONSerialization.data(withJSONObject: from, options: .prettyPrinted)
        let decoder = JSONDecoder()
        self = try decoder.decode(Self.self, from: data)
      }
    }
    

    根据问题中的示例,结果将是

    let person = Person(from: dictionary)
    

    如果您有兴趣采取另一种方式,那么这可能会有所帮助https://stackoverflow.com/a/46329055/1453346

    【讨论】:

    • 什么是 DateFormatter 部分...?
    • @Marty:使用 Codable,您可以在 Decorder 中定义自己的日期格式,以便更正设置对象的日期属性。
    • @smukamuka 是的,但在这种特殊情况下,它与问题有什么关系......? :)
    • 没什么!只是我的特殊问题有一个日期,而 json 序列化自动编码日期和可解码的事实一开始并没有让我感到困惑,所以我把它留了下来
    • 这是一个很好的答案。如果您来自stackoverflow.com/a/46329055/1453346,则应删除日期格式化程序行,它们会破坏该用例中的解码
    【解决方案2】:

    基于Chris Mitchelmore answer

    详情

    • Xcode 版本 10.3 (10G8),Swift 5

    解决方案

    import Foundation
    
    extension Decodable {
    
        init(from value: Any,
             options: JSONSerialization.WritingOptions = [],
             decoder: JSONDecoder) throws {
            let data = try JSONSerialization.data(withJSONObject: value, options: options)
            self = try decoder.decode(Self.self, from: data)
        }
    
        init(from value: Any,
             options: JSONSerialization.WritingOptions = [],
             decoderSetupClosure: ((JSONDecoder) -> Void)? = nil) throws {
            let decoder = JSONDecoder()
            decoderSetupClosure?(decoder)
            try self.init(from: value, options: options, decoder: decoder)
        }
    
        init?(discardingAnErrorFrom value: Any,
              printError: Bool = false,
              options: JSONSerialization.WritingOptions = [],
              decoderSetupClosure: ((JSONDecoder) -> Void)? = nil) {
            do {
                try self.init(from: value, options: options, decoderSetupClosure: decoderSetupClosure)
            } catch {
                if printError { print("\(Self.self) decoding ERROR:\n\(error)") }
                return nil
            }
        }
    }
    

    用法

    struct Item: Decodable {
        let id: Int
        let name: String
        let isActive: Bool
        var date: Date
    }
    
    let dictionary = ["id": 1, "name": "Item", "is_active": false,
                      "date": "2019-08-06T06:55:00.000-04:00"] as [String : Any]
    do {
        let item1 = try Item(from: dictionary) { decoder in
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
            decoder.dateDecodingStrategy = .formatted(dateFormatter)
        }
        print(item1)
    } catch {
        print("Error: \(error)")
    }
    
    print("\n========================")
    let item2 = Item(discardingAnErrorFrom: dictionary)
    print(String(describing: item2))
    
    print("\n========================")
    let item3 = Item(discardingAnErrorFrom: dictionary, printError: true)
    print(String(describing: item3))
    
    print("\n========================")
    let item4 = Item(discardingAnErrorFrom: dictionary){ decoder in
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        decoder.dateDecodingStrategy = .formatted(dateFormatter)
    }
    print(String(describing: item4))
    

    使用记录

    Item(id: 1, name: "Item", isActive: false, date: 2019-08-06 10:55:00 +0000)
    
    ========================
    nil
    
    ========================
    Item decoding ERROR:
    keyNotFound(CodingKeys(stringValue: "isActive", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"isActive\", intValue: nil) (\"isActive\").", underlyingError: nil))
    nil
    
    ========================
    Optional(__lldb_expr_5.Item(id: 1, name: "Item", isActive: false, date: 2019-08-06 10:55:00 +0000))
    

    【讨论】:

      【解决方案3】:

      我改编了Chris Mitchelmore 的答案,使其成为一个可失败的初始化程序,而不是抛出代码。在某些情况下让它更方便一些。

      extension Decodable {
          init?(from: Any) {
              guard let data = try? JSONSerialization.data(withJSONObject: from, options: .prettyPrinted) else { return nil }
              let decoder = JSONDecoder()
              guard let decoded = try? decoder.decode(Self.self, from: data) else { return nil }
              self = decoded
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-04
        • 1970-01-01
        • 2018-06-22
        相关资源
        最近更新 更多