【问题标题】:Using Swift 4's Decodable protocol for nested JSON server response使用 Swift 4 的可解码协议进行嵌套的 JSON 服务器响应
【发布时间】:2018-03-03 00:37:54
【问题描述】:

我正在尝试实现 Swift 4 的新 Decodable 协议,但遇到了一些困难。

这是我的 JSON 服务器响应:

{
  "success": true,
  "errorCode": 0,
  "message": "Succcess",
  "data": {
    "name": "Logan Howlett",
    "nickname": "The Wolverine",
    "image": "http://heroapps.co.il/employee-tests/ios/logan.jpg",
    "dateOfBirth": 1880,
    "powers": [
      "Adamantium Bones",
      "Self-Healing",
      "Adamantium Claws"
    ],
    "actorName": "Hugh Jackman",
    "movies": [
      {
        "name": "X-Men Origins: Wolverine",
        "year": 2009
      },
      {
        "name": "The Wolverine",
        "year": 2013
      },
      {
        "name": "X-Men: Days of Future Past",
        "year": 2014
      },
      {
        "name": "Logan",
        "year": 2017
      },
    ]
  }
}

解码响应的data 部分的最佳方法是什么? 另外,如果data 突然变成array 而不是对象,我该如何支持这两种数据类型?

非常感谢:)

【问题讨论】:

  • Also, what happens if the data is suddenly an array instead of an object, how can I support both data types? — 使用 enums 与您类型中的关联值?一种情况是array,另一种情况是dictionary
  • 您应该通读 Codable 文档中的 Encoding and Decoding Custom Types 以了解如何编写反映您的 JSON 表示的类型。这主要涉及编写类型并符合 Codable,其余的应该为您完成。
  • 对于那些关注的人。另一个好书 (medium.com/swiftly-swift/…)

标签: swift swift4 codable decodable


【解决方案1】:

首先你可以创建一个扩展作为助手:

extension Data {
    func decode <Generic: Codable> () -> Generic? {
        let decoder = JSONDecoder()
        let object = try? decoder.decode(Generic.self, from: self)
        return object
    }
}


extension Dictionary {
    func decode <Generic: Codable> () -> Generic? {
        let data = try? JSONSerialization.data(withJSONObject: self,
                                               options: JSONSerialization.WritingOptions.prettyPrinted)
        guard let d = data else {
            return nil
        }
        return d.decode()
    }
}

然后你可以创建一个协议来帮助你构建你的对象:

protocol Encode: Codable {
    init(with dictionary: [String: Any])
    init(with data: Data)
}

使用默认实现:

extension Encode {
    init(with data: Data) {
        let object: Self? = data.decode()
        guard let obj = object else {
            fatalError("fail to init object with \(data)")
        }
        self = obj
    }

    init(with dictionary: [String: Any]) {
        let object: Self? = dictionary.decode()
        guard let obj = object else {
            fatalError("fail to init object with \(dictionary)")
        }
        self = obj
    }

然后将您的对象创建为符合 y Codable 协议的结构。它看起来像:

struct User: Codable {
    var name: String?
    var nickname: String?
    ...
    // If needed declare CodingKey here
    // enum CodingKeys: String, CodingKey {
    //     case date = "dateOfBirth"
    //     ...
    // }
}

struct Movies: Codable {
    var name: String?
    var year: Int?
}

现在您需要从响应中提取数据字典并应用新的 init 方法:

if let dic = json["data"] as? [String: Any] {
    let user: User = User(with: dic)
    // Do stuff here
}

如果数据突然变成一个数组,你将不得不以不同的方式处理它(在这个例子中是一个用户数组)

【讨论】:

  • 请在评论中解释为什么我的答案不合适,以便我可以替换或改进它。谢谢
  • 我不知道是谁对你的答案投了反对票,但不是我。明天早上我会读它并尝试它。会让你知道进展如何:)
猜你喜欢
  • 1970-01-01
  • 2018-01-25
  • 2017-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多