【问题标题】:How to use Alamofire with Codable protocol and parse data如何将 Alamofire 与 Codable 协议一起使用并解析数据
【发布时间】:2020-02-11 12:53:14
【问题描述】:

第一次使用 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
    }
]   
} 

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

【问题讨论】:

    标签: ios swift5 xcode11 codable decodable


    【解决方案1】:

    问题在于您的结构,结构中属性的名称应与 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 的确切类型并将其设为可选,否则会出现错误,没有类型,所以我在其中放置了一个可选字符串。

    希望这会有所帮助。

    【讨论】:

    • 仍然出现错误:keyNotFound(CodingKeys(stringValue: "userId", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"userId\", intValue: nil) (\"userId\").", underlyingError: nil))
    • 那不是因为它是可选的,但在我的代码中不是可选的,更改你知道的值可能是 nil 并且你很高兴,我会更新用户 ID
    • 感谢您提供问题的原因并提供解决方案的指导。
    【解决方案2】:

    问题在于,在您的顶级 JSON 中,您只有一个 array 类型的“数据”属性。

    您要求 JSONDecoder 将仅包含“数据”属性的 JSON 对象解码为名为“MyTasks”的 Swift 对象,其中包含您定义的存储属性(包括 strDateDay)。

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

    你必须为反序列化创建一个顶级对象。类似的东西:

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

    然后就像你已经做的那样把它交给你的 JSONDecoder :

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

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

    【讨论】:

    • 为什么我无法访问“getMyTasks?.data.name”
    • 您显示的 JSON 是您定义的对象的数组。你解释说你想把它变成一个单一的对象,那是不可能的。您的“MyTasks”对象实际上是一个“Task”对象,包含在“Response”对象的“data”属性中。您只能通过访问 yourMainObject.data[0] 等来访问您的任务
    • 别说了,我弄错了。请让我知道如何将“getMyTasks?.data”添加到数组“var arrDayTasks = [[String: Any]]()”
    • "getMyTasks?.data" 已经是一个数组,像访问任何数组一样访问您的对象......我不明白你的意思抱歉。看看这篇文章的结尾,它已经解释过了,这正是你的情况:learnappmaking.com/codable-json-swift-how-to
    • 感谢您的回答也有帮助。
    【解决方案3】:

    我建议使用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&lt;[MyTasksResponseModel]&gt;解码数组

    使用 APIRequest 解码一个对象

    【讨论】:

      猜你喜欢
      • 2021-10-08
      • 2019-04-11
      • 1970-01-01
      • 2019-07-16
      • 1970-01-01
      • 1970-01-01
      • 2018-11-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多