如何将 Alamofire 与 Codable 协议一起使用并解析数据

How to use Alamofire with Codable protocol and parse data

第一次用Codable协议,很多东西不太清楚。在阅读了几个教程之后,开始使用 Codable 来解析数据。下面是代码:

struct MyTasks : Codable {
var strDateDay : String = ""
var strReminder : String = ""
var strRepetitions : String = ""
var name : String = ""
var strNotes : String = ""
var strUserId : String = ""

private enum CodingKeys: String, CodingKey {
    case strDateDay = "duedate"
    case strReminder = "reminder"
    case strRepetitions = "recurring"
    case name = "name"
    case strNotes = "notes"
    case strUserId = "userId"

}
}

let networkManager = DataManager()
    networkManager.postLogin(urlString: kGetMyTasks, header: header, jsonString: parameters as [String : AnyObject]) { (getMyTasks) in

        print(getMyTasks?.name as Any) // -> for log
        Common_Methods.hideHUD(view: self.view)
    }

// 网络管理员:

func postLogin(urlString: String, header: HTTPHeaders, jsonString:[String: AnyObject], completion: @escaping (MyTasks?) -> Void) {

           let apiString = KbaseURl + (kGetMyTasks as String)
           print(apiString)

                Alamofire.request(apiString, method: .post, parameters: jsonString , encoding: URLEncoding.default, headers:header).responseJSON
                { response in

                let topVC = UIApplication.shared.keyWindow?.rootViewController

                DispatchQueue.main.async {
                    //Common_Methods.showHUD(view: (topVC?.view)!)
                }

                guard let data = response.data else { return }
                do {
                    let decoder = JSONDecoder()
                    let loginRequest = try decoder.decode(MyTasks.self, from: data)
                    completion(loginRequest)
                } catch let error {
                    print(error)
                    completion(nil)
                }
            }
    }

现在,这是回复:

keyNotFound(CodingKeys(stringValue: "strDateDay", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"strDateDay\", intValue: nil) (\"strDateDay\").", underlyingError: nil))

下面是我试图解析的 json 响应:

{
"data": [
    {
        "userId": 126,
        "name": "My task from postman ",
        "notes": null,
        "totalSteps": 0,
        "completedSteps": 0,
        "files": 0
    },
    {
        "userId": 126,
        "name": "My task from postman 1",
        "notes": null,
        "totalSteps": 0,
        "completedSteps": 0,
        "files": 0
    }
]   
} 

我知道我遗漏了一些东西,但即使花了半天多的时间,我也没有正确理解哪里出了问题。请指导。

问题出在你的结构上,结构中属性的名称应该与 json 数据匹配,或者如果你想使用自定义名称,你应该使用 enum CodingKeys 将它们转换为你喜欢的

struct MyTasks: Codable {
    let data: [Datum]
}

struct Datum: Codable {
    let userID: Int?
    let name: String
    let notes: String?
    let totalSteps, completedSteps, files: Int

    enum CodingKeys: String, CodingKey {
        case userID = "userId"
        case name, notes, totalSteps, completedSteps, files
    }
 }

    func postLogin(urlString: String, header: HTTPHeaders, jsonString:[String: AnyObject], completion: @escaping (MyTasks?) -> Void) {

       let apiString = KbaseURl + (kGetMyTasks as String)
       print(apiString)

        Alamofire.request(apiString, method: .post, parameters: jsonString , encoding: URLEncoding.default, headers:header).responseJSON
        { response in

            let topVC = UIApplication.shared.keyWindow?.rootViewController

            DispatchQueue.main.async {
                //Common_Methods.showHUD(view: (topVC?.view)!)
            }

            guard let data = response.data else { return }
            do {
                let decoder = JSONDecoder()
                let loginRequest = try decoder.decode(MyTasks.self, from: data)
                completion(loginRequest)
            } catch let error {
                print(error)
                completion(nil)
            }
        }
}

还有一件事要记住,你知道 notes 的确切类型并使其成为可选的,否则会出现错误,没有类型所以我在那里放了一个可选的字符串。

希望这会有所帮助。

问题是在你的顶部 JSON 你有一个 "data" 属性 类型 array.

您要求 JSON解码器将仅包含 "data" 属性 的 JSON 对象解码为名为 "MyTasks" 的 Swift 对象您定义的存储属性(包括 strDateDay)。

因此解码器向您发送此响应是因为他无法在该顶部对象中找到 strDateDay。

您必须创建一个顶级对象以进行反序列化。像 :

struct MyResponse: Codable {
   var data: [MyTasks]
}

然后就像您已经完成的那样将它提供给您的JSON解码器:

let loginRequest = try decoder.decode(MyResponse.self, from: data)

您发送给解码器的数据是您的完整 JSON 流(Alamofire 响应对象的数据 属性),而不仅仅是 "data" 属性你的 JSON 结构。

我建议使用 CodyFire lib 因为它支持与请求相关的所有 Codable。

您的 POST 请求可能看起来像

struct MyTasksPayload: JSONPayload {
    let param1: String
    let param2: String
}

struct MyTasksResponseModel: Codable {
    let strDateDay: String
    let strReminder: String
    let strRepetitions: String
    let name: String
}

let server = ServerURL(base: "https://server1.com", path: "v1")

APIRequest<[MyTasksResponseModel]>(server, "mytasks", payload: payloadModel)
    .method(.post)
    .onRequestStarted {
        // show HUD here
    }
    .onError {
        // hide hud and show error here
    }
    .onSuccess { tasks in
        // here's your decoded tasks
        // hide hud here
    }

使用APIRequest<[MyTasksResponseModel]>解码数组

使用API​​Request解码一个对象