如何解码来自 URLSession 的 http 响应数据并映射到响应结构 swift

How to decode an http response data from URLSession and map to a response structure swift

我是 swift 编程的新手..能够从 URLSession 获得成功的响应,但我无法将数据对象解析(解码)为我想要的 APIResponse 结构

这是我的url请求代码:

func load(urlRequest: URLRequest, withCompletion completion: @escaping (_ response: APIResponse) -> Void) {
        
        let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            
            guard error == nil else {
                print("Error fetching data from server\nERROR: \(String(describing: error))")
                return
            }
            
            guard let jsonData = data else {
                print("Response Data is empty")
                return
            }
            
            printResponseBody(response: data)
            
            let decoder = JSONDecoder()
            let response = try? decoder.decode(APIResponse.self, from: jsonData)
            
            guard let decodedResponse = response else {
                print("Unable to parse data from response")
                return
            }
            
            print("Decoded Response: ", decodedResponse)
            
            DispatchQueue.main.async { completion(decodedResponse) }
        }
        
        task.resume()
    }

这是我的响应结构,我需要将响应数据映射到该结构以在我的代码中使用:

struct APIResponse: Codable {
    
    let responseCode: Int
    let data: ResultSet
    let meta: Meta

    enum CodingKeys: String, CodingKey {
        case responseCode = "response_code"
        case data, meta
    }
}

// MARK: - DataClass
struct ResultSet: Codable {
    
    let appVersionUpdate: String
    let offers: [Offer]
    let rate: Int

    enum CodingKeys: String, CodingKey {
        case appVersionUpdate = "app_version_update"
        case offers, rate
    }
}

// MARK: - Offer
struct Offer: Codable, Identifiable {
    
    let id: Int
    let title: String
    let image: String?
    let r, resultCount: Double

    enum CodingKeys: String, CodingKey {
        case id, r = "r"
        case title, image
        case resultCount = "result_count"
    }
}

// MARK: - Meta
struct Meta: Codable {
    
    let apiVersion: Int

    enum CodingKeys: String, CodingKey {
        case apiVersion = "api_version"
    }

这是我要解码的来自服务器的json

 {
    "response_code": 0,
    "data": {
        "app_version_update": "",
        "offers": [
            {
                "title": "Special Scheme1",
                "image": "http://59.145.109.138:11101/Offers/BGL_banner_1080_x_540_1.jpg",
                "r": 1.0,
                "result_count": 5.0
            },
            {
                "title": "test 1",
                "image": "http://59.145.109.138:11101/Offers/Yoho-National-Park2018-10-27_10-10-52-11.jpg",
                "r": 2.0,
                "result_count": 5.0
            },
            {
                "title": "Offer Test 1234444",
                "image": "http://59.145.109.138:11101/Offers/Stanley-Park2018-10-27_10-11-27-44.jpg",
                "r": 3.0,
                "result_count": 5.0
            }
        ],
        "rate": 2000
    },
    "meta": {
        "api_version": 2.0
    }
 }

每当我 运行 此代码时,我都会收到“无法从响应中解析数据”错误。如果有人告诉我我在这里做错了什么,我将不胜感激

问题在于解码 Offer 中的 id。用这个替换你的Offer

struct Offer: Codable, Identifiable {
    
    let id: Int
    let title: String
    let image: String?
    let r, resultCount: Int

    enum CodingKeys: CodingKey {
        case id, r, title, image, resultCount
        
        var stringValue: String {
            switch self {
            case .id, .r: return "r"
            case .title: return "title"
            case .image: return "image"
            case .resultCount: return "result_count"
            }
        }
    }
}

备注

  • 首先,你不应该摆脱这个错误。相反,您可以打印错误并尝试找出问题所在。
  • 如果您声明 enum CodingKeys: String, CodingKey,每种情况的原始值 必须 与其他情况不同,否则 Xcode 会报错。在你的例子中,它没有抱怨,因为 idIdentifiable 协议的要求,但它甚至没有使用你为 id 设置的原始值。如果你想对 2 个不同的变量使用相同的键,你应该做我上面做的同样的事情。

更好的代码

这将与您的代码一样工作,但更简洁:

struct Offer: Codable, Identifiable {
    
    var id: Int { r }
    let title: String
    let image: String?
    let r, resultCount: Int

    enum CodingKeys: String, CodingKey {
        case r, title, image, resultCount
    }
}

它基本上说每当你需要 id 时,从 r 获取它。你也可以像我一样去掉CodingKeys中的stringValue,改用CodingKeys: String构象。