Swift中如何用Alamofire手动解析嵌套数组JSON到数据模型
Swift + Alamofire 嵌套JSON解析问题解决
最近开始学习Swift,用Alamofire框架解析带嵌套数组的JSON到自定义数据模型时遇到错误,以下是相关代码及解决方案。
相关代码
JSON 响应内容
(注:原内容格式修正为标准JSON)
{ "type": "product", "number": 10, "offset": 0, "processingTimeMs": 4735, "products": [ { "id": 10026738, "image": "https://spoonacular.com/productImages/10026738-312x231.jpg", "imageType": "jpg", "title": "Jenos by totinos crsp n tsty pizza for 1 combo" }, { "id": 8215206, "image": "https://spoonacular.com/productImages/8215206-312x231.jpg", "imageType": "jpg", "title": "Supreme sausage, pepperoni, green, red and yellow peppers, roasted garlic & red onions brick oven crust pizza, supreme" }, { "id": 9822788, "image": "https://spoonacular.com/productImages/9822788-312x231.jpg", "imageType": "jpg", "title": "Grisinetos pizza" } ], "totalProducts": 16410 }
数据模型
struct Product: Decodable { let id: Int let title: String let imageType: String } struct Grocery: Decodable { let products: [Product] let totalProducts: Int let type: String let offset: Int let number: Int }
原请求函数
private func fetchProducts() { AF.request(groceryLink) .validate() .responseJSON { dataResponse in switch dataResponse.result { case .success(let value): guard let groceryDetails = value as? [String: Any] else { return } var products: [Product] = [] for productDetails in groceryDetails["products"] { let product = Product( id: productDetails["id"] as? Int ?? 0, title: productDetails["title"] as? String ?? "", imageType: productDetails["imageType"] as? String ?? "" ) products.append(product) } let grocery = Grocery(products: products, totalProducts: groceryDetails["totalProducts"] as? Int ?? 0, type: groceryDetails["type"] as? String ?? "", offset: groceryDetails["offset"] as? Int ?? 0, number: groceryDetails["number"] as? Int ?? 0) case .failure(let error): print(error) } } }
错误原因分析
- 类型转换遗漏:
groceryDetails["products"]返回的是Any?类型,直接遍历会报错,必须先强制转换为[[String: Any]]才能进行循环操作。 - 冗余手动解析:已经定义了遵循
Decodable的模型,完全可以利用Alamofire的自动解析能力,无需手动逐个字段赋值,既麻烦又容易出错。
解决方案
方案一:修复手动解析(不推荐)
若坚持手动解析,需补全类型转换步骤:
private func fetchProducts() { AF.request(groceryLink) .validate() .responseJSON { dataResponse in switch dataResponse.result { case .success(let value): guard let groceryDetails = value as? [String: Any], let productArray = groceryDetails["products"] as? [[String: Any]] else { return } var products: [Product] = [] for productDetails in productArray { let product = Product( id: productDetails["id"] as? Int ?? 0, title: productDetails["title"] as? String ?? "", imageType: productDetails["imageType"] as? String ?? "" ) products.append(product) } let grocery = Grocery(products: products, totalProducts: groceryDetails["totalProducts"] as? Int ?? 0, type: groceryDetails["type"] as? String ?? "", offset: groceryDetails["offset"] as? Int ?? 0, number: groceryDetails["number"] as? Int ?? 0) // 后续处理grocery对象 case .failure(let error): print(error) } } }
方案二:使用Alamofire自动解析(推荐)
利用responseDecodable直接将JSON解析为自定义模型,代码更简洁可靠:
private func fetchProducts() { AF.request(groceryLink) .validate() .responseDecodable(of: Grocery.self) { dataResponse in switch dataResponse.result { case .success(let grocery): // 直接拿到解析好的Grocery对象,后续处理 print(grocery.products.count) print(grocery.totalProducts) case .failure(let error): print("解析错误:\(error)") } } }
额外说明
- 确保JSON响应字段名与模型字段完全匹配(Swift默认大小写敏感,若需映射不同字段名,可添加
CodingKeys枚举)。 validate()方法会自动检查HTTP状态码是否在200-299范围内,非成功状态码会进入failure分支。
内容的提问来源于stack exchange,提问作者maxMas
相关产品推荐
相关产品推荐

