You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
        }
    }
}

错误原因分析

  1. 类型转换遗漏:groceryDetails["products"] 返回的是Any?类型,直接遍历会报错,必须先强制转换为[[String: Any]]才能进行循环操作。
  2. 冗余手动解析:已经定义了遵循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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 03:18:37