如何将JSON响应解析为模型并加载到tableview中?

问题描述

我有一个JSON响应,如下所示。

{
    "data": [
        {
            "division": "Chemical","divisionid": 5
        },{
            "division": "Corporate","divisionid": 12
        },{
            "division": "Drilling","divisionid": 1
        },{
            "division": "Energy","divisionid": 2
        }
    ],"response": {
        "code": "413","message": "Success","status": "True","userToken": "XXXX"
    }
}

我的模型类如下

struct ApiResponse<T: Codable>: Codable {
    let data: T
    let response: Response
}

struct Division: Codable {
    let divisionid: Int
    let division: String

}

struct Response: Codable {
    let code: String
    let message: String
    let status: String
    let userToken: String
} 

我正在如下所示呼叫Api

呼叫部分

getDivisions () { result in
                    switch result {
                    case .success(let divisions):
                        print(divisions)
                    case .failure(let error):
                        print("Error: \(error.localizedDescription)")
                    }
                }

定义部分

func getDivisions(completed: @escaping (Result<[Division],ApiErrors>) -> Void) {
    
    let endpoint = BaseUrl + ApiEndPoint.DivisionsList.description
    
    guard let url = URL(string: endpoint) else {
        completed(.failure(.invalidUsername))
        return
    }
    guard (String(describing: UserDefaults.standard.string(forKey: "AuthCode"))) != "" else {
        completed(.failure(.invalidAuthToken))
        return
    }
    
    let localParameters: [String: Any] = [
        "userToken": (UserDefaults.standard.string(forKey: "userTokenBI")) ?? "Default"
    ]
    
    print("Auth Code retrieved: \(UserDefaults.standard.string(forKey: "authCodeUTS") ?? "Failed to get value")")
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json; charset=utf-8",forHTTPHeaderField: "Content-Type")  // the request is JSON
    request.setValue("application/json; charset=utf-8",forHTTPHeaderField: "Accept")        // the expected response is also JSON
    request.setValue("Basic \(String(describing: UserDefaults.standard.string(forKey: "authCodeUTS")))",forHTTPHeaderField: "Authorization")
    request.timeoutInterval = 60.0
    
    do {
        request.httpBody = try JSONSerialization.data(withJSONObject: localParameters,options: .prettyPrinted)
    } catch let error {
        print(error.localizedDescription)
    }
    
    let task = URLSession.shared.dataTask(with: request) { data,response,error in
        
        if let _ = error {
            completed(.failure(.unabletoComplete))
            //return
        }
        //HTTP here
                
        guard let data = data else {
            completed(.failure(.invalidData))
            return
        }

        do {
            let response = try JSONDecoder().decode(ApiResponse<[Division]>.self,from: data)
                completed(.success(response.data))
          } catch {
                print(error)
                completed(.failure(.invalidData))
          }
    }
    task.resume()
}

但是由于解析未正确完成,我无法将数据从JSON响应返回到调用部分。我得到的错误如下

dataCorrupted(Swift.DecodingError.Context(codingPath: [],debugDescription: "The given data was not valid JSON.",underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))
Error: The operation Couldn’t be completed.

有人可以帮助我解决我面临的问题吗?

预先感谢

解决方法

我建议通过删除不必要的编码键和初始化程序,如下创建数据类型,

struct ApiResponse: Codable {
    let data: [Division]
    let response: Response
}

struct Division: Codable {
    let deptcode: String
    let deptname: String
}

// MARK: - Response
struct Response: Codable {
    let code: String
    let message: String
    let status: String
    let userToken: String
} 

并如下更新getDivision方法签名和解码,

func getDivisions(completed: @escaping (Result<[Division],ApiErrors>) -> Void) {
    //...
    //...

     do {
          let response = try JSONDecoder().decode(ApiResponse.self,from: data)
           completed(.success(response.data))
      } catch {
            print(error)
            completed(.failure(.invalidData))
      }
}

由于ApiResponse数据将是通用数据,因此更好的解决方案是将其声明为以下内容

struct ApiResponse<T: Codable>: Codable {
    let data: T
    let response: Response
}

现在您可以解析API期望的任何数据类型,

 do {
      let response = try JSONDecoder().decode(ApiResponse<[Division]>.self,from: data)
       completed(.success(response.data))
  } catch {
        print(error)
        completed(.failure(.invalidData))
  }

用法:

尝试以下作为didFinishLaunchingWithOptionsAppDelegate的证明

        let data = """
{
    "data": [
        {
            "division": "Chemical","divisionid": 5
        },{
            "division": "Corporate","divisionid": 12
        },{
            "division": "Drilling","divisionid": 1
        },{
            "division": "Energy","divisionid": 2
        }
    ],"response": {
        "code": "413","message": "Success","status": "True","userToken": "XXXX"
    }
}
""".data(using: .utf8)!

do {
     let response = try JSONDecoder().decode(ApiResponse<[Division]>.self,from: data)
    response.data.forEach { print($0.division,$0.divisionid) }
 } catch {
       print(error)
 }

// prints
Chemical 5
Corporate 12
Drilling 1
Energy 2