17

所以我有一个返回 JSON 对象数组的 API 路由。例如:

[
    {"firstname": "Tom", "lastname": "Smith", "age": 31},
    {"firstname": "Bob", "lastname": "Smith", "age": 28}
]

我正在尝试设想如何使用 Swift 中的新可编码功能将它们转换为一个类中的两个对象。因此,如果我有一个可编码的 person 类,我希望得到该响应并让它给我两个 person 对象。

我也在使用 Alamofire 来处理请求。

我怎样才能做到这一点?到目前为止,我所看到的与可编码内容相关的所有内容都只允许 1 个对象。而且我还没有看到与 Alamofire 或 Web 框架的任何集成。

4

4 回答 4

19

关于 Alamofire 5 的更新:responseJSONDecodable.

struct Person: Codable {
    let firstName, lastName: String
    let age: Int

    enum CodingKeys : String, CodingKey {
        case firstName = "firstname"
        case lastName = "lastname"
        case age
    }
}

Alamofire.request(request).responseJSONDecodable { (response: DataResponse<Person>) in
    print(response)
}

Alamofire 4 暂时不会添加 Codable 支持(请参阅#2177),您可以改用此扩展程序:https ://github.com/Otbivnoe/CodableAlamofire 。

let jsonData = """
[
    {"firstname": "Tom", "lastname": "Smith", "age": 31},
    {"firstname": "Bob", "lastname": "Smith", "age": 28}
]
""".data(using: .utf8)!

struct Person: Codable {
    let firstName, lastName: String
    let age: Int

    enum CodingKeys : String, CodingKey {
        case firstName = "firstname"
        case lastName = "lastname"
        case age
    }
}

let decoded = try! JSONDecoder().decode([Person].self, from: jsonData)

示例:http ://swift.sandbox.bluemix.net/#/repl/59a4b4fad129044611590820

使用 CodableAlamofire:

let decoder = JSONDecoder()
Alamofire.request(url).responseDecodableObject(keyPath: nil, decoder: decoder) { (response: DataResponse<[Person]>) in
    let persons = response.result.value
    print(persons)
}

keypath对应于结果包含在 JSON 结构中的路径。例如:

{
    "result": {
        "persons": [
            {"firstname": "Tom", "lastname": "Smith", "age": 31},
            {"firstname": "Bob", "lastname": "Smith", "age": 28}
        ]
    }
}

keypath=>results.persons

[
    {"firstname": "Tom", "lastname": "Smith", "age": 31},
    {"firstname": "Bob", "lastname": "Smith", "age": 28}
]

keypath=> nil(空keypath抛出异常)

于 2017-08-29T00:25:10.447 回答
0

我设法将数据响应序列化为可编码对象。

例如,您可能已经熟悉转换 json 对象[String: String]。该 json 对象需要Data使用json.data(using: .utf8)!.

使用 Alamofire,很容易获得该数据(或者至少这种数据对我有用,已经与.utf8事物兼容),我可以使用这个已经可用的功能

func responseData(queue: DispatchQueue?, completionHandler: @escaping (DataResponse<Data>) -> Void) -> Self

Decoder然后只需使用该数据作为输入completionHandler

let objek = try JSONDecoder().decode(T.self, from: data)

您还可以从文档中进行一些通用的序列化功能,只需稍作调整

通用响应对象序列化

对此修改

func responseCodable<T: Codable>(
    queue: DispatchQueue? = nil,
    completionHandler: @escaping (DataResponse<T>) -> Void)
    -> Self
{
    let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
        guard error == nil else { return .failure(BackendError.network(error: error!)) }

        guard let data = data else {
            return .failure(BackendError.objectSerialization(reason: "data is not valid"))
        }


        do{
            let objek = try JSONDecoder().decode(T.self, from: data!)
            return .success(objek)
        } catch let e {
            return .failure(BackendError.codableSerialization(error: e))
        }

    }

    return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
}

示例结构

struct Fids: Codable {

   var Status: Status?
   var Airport: Airport?
   var Record: [FidsRecord]
}

以这种方式使用该功能

    Alamofire.request("http://whatever.com/zzz").responseCodable { (response: DataResponse<Fids>) in
        switch response.result{
        case .success(let value):
            print(value.Airport)
        // MARK: do whatever you want
        case .failure(let error):
            print(error)
            self.showToast(message: error.localizedDescription)
        }
    }
于 2017-11-18T06:14:41.767 回答
0

要解码为数组,为清楚起见,您需要在类型别名中做出响应:

typealias ServiceResponseObject = [ResponseObject]

但随后您必须确认 Array 可编码:

extension Array: Decodable where Element: Decodable {}

这应该使一切正常。

于 2019-10-16T14:10:23.933 回答
0

Swift 5 使用 Codable

Alamofire 通用响应

PersonModel.swift(使用 SwiftyJsonAccelerator 创建)

import Foundation

class PersonModel: Codable {

  enum CodingKeys: String, CodingKey {
    case age
    case firstname
    case lastname   }

  var age: Int?   var firstname: String?   var lastname: String?

  init (age: Int?, firstname: String?, lastname: String?) {
    self.age = age
    self.firstname = firstname
    self.lastname = lastname   }

  required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    age = try container.decodeIfPresent(Int.self, forKey: .age)
    firstname = try container.decodeIfPresent(String.self, forKey: .firstname)
    lastname = try container.decodeIfPresent(String.self, forKey: .lastname)   }

}

通用获取响应

func genericGET<T: Decodable>(urlString: String, completion: @escaping (T?) -> ()) {

Alamofire.request(urlString)
    .responseJSON { response in
        // check for errors
        switch response.result {
        case .success(_):

            do {
                let obj = try JSONDecoder().decode(T.self, from: response.data!)
                completion(obj)
            } catch let jsonErr {
                print("Failed to decode json:", jsonErr)
            }

            break
        case .failure(_):


            completion(nil)

            break
        }
}

}

调用这个方法

genericGET(urlString: "YOUR_URL") { (persons: [PersonModel]?) in
    print(persons?[0].firstname)
}
于 2020-01-31T09:56:00.933 回答