Ios 在Alamofire中使用get请求时,如何修复swift中无效的JSON响应?
我正在创建一个应用程序,其中它从API服务器中提取Ios 在Alamofire中使用get请求时,如何修复swift中无效的JSON响应?,ios,swift,rest,api,alamofire,Ios,Swift,Rest,Api,Alamofire,我正在创建一个应用程序,其中它从API服务器中提取PatientList,并将显示到表视图中。检查后,它返回200状态代码,但返回invalidJSON错误。但是当我签入Postman时,它返回200状态码并正确地提取记录。因为我是swift新手,所以我很困惑我的代码的哪一部分导致了错误。我正在寻求帮助来解决这个问题。以下是我的示例代码供您参考。事先非常感谢你 耐心。迅速 struct Patient: Codable { let hospitalNumber: Int let
PatientList
,并将显示到表视图中。检查后,它返回200
状态代码,但返回invalidJSON
错误。但是当我签入Postman时,它返回200
状态码并正确地提取记录。因为我是swift新手,所以我很困惑我的代码的哪一部分导致了错误。我正在寻求帮助来解决这个问题。以下是我的示例代码供您参考。事先非常感谢你
耐心。迅速
struct Patient: Codable {
let hospitalNumber: Int
let patientName: String
let totalAmount: Double
enum CodingKeys: String, CodingKey {
case hospitalNumber = "hospitalNumber"
case patientName = "patientName"
case totalAmount = "totalAmount"
}
}
struct PatientList {
typealias getPatientListTaskCompletion = (_ patientListperPayout: [Patient]?, _ error: NetworkError?) -> Void
static func getPatientList(doctorNumber: Int, periodId: Int, completion: @escaping getPatientListTaskCompletion) {
guard let patientPerPayoutURL = URL(string: "\(Endpoint.Patient.patientPerPayout)?periodId=\(periodId)&doctorNumber=\(doctorNumber)") else {
completion(nil, .invalidURL)
return
}
let sessionManager = Alamofire.SessionManager.default
sessionManager.session.getAllTasks { (tasks) in
tasks.forEach({ $0.cancel() })
}
Alamofire.request(patientPerPayoutURL, method: .get, encoding: JSONEncoding.default).responseJSON { (response) in
guard HelperMethods.reachability(responseResult: response.result) else {
completion(nil, .noNetwork)
return
}
guard let statusCode = response.response?.statusCode else {
completion(nil, .noStatusCode)
return
}
switch(statusCode) {
case 200:
guard let jsonData = response.data else{
completion(nil, .invalidJSON)
print(statusCode)
return
}
let decoder = JSONDecoder()
do {
let patientListArray = try decoder.decode([Patient].self, from: jsonData)
let sortedPatientListArray = patientListArray.sorted(by: { $0.patientName < $1.patientName })
completion(sortedPatientListArray, nil)
}catch{
completion(nil, .invalidJSON)
print(statusCode)
}
case 400:
completion(nil, .badRequest)
case 404:
completion(nil, .noRecordFound)
default:
print("UNCAPTURED STATUS CODE FROM getPatientList\nSTATUS CODE: \(statusCode)")
completion(nil, .uncapturedStatusCode)
}
}
}
var patientList: [Patient]! {
didSet {
performSegue(withIdentifier: patientListIdentifier, sender: self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.latestCreditedAmountTableView.dataSource = self
self.latestCreditedAmountTableView.delegate = self
configureTableViewCell()
showTotalCreditedAmount()
getDoctorPayoutSummary(doctorNumber: doctorNumber)
}
func getDoctorPayoutSummary(doctorNumber: Int) {
self.payoutSummary = payoutSummaryDetails
self.taxRateVatRateLabel.text = "\(self.payoutSummary.taxRate) / \(self.payoutSummary.vatRate)"
self.getPatientList()
self.latestCreditedAmountTableView.reloadData()
return
}
func getPatientList() {
APIService.PatientList.getPatientList(doctorNumber: doctorNumber, periodId: currentRemittance.periodId) { (patientListArray, error) in
guard let patientListPerPayout = patientListArray, error == nil else {
if let networkError = error {
switch networkError {
case .noRecordFound:
let alertController = UIAlertController(title: "No Record Found", message: "You don't have current payment remittance", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
case .noNetwork:
let alertController = UIAlertController(title: "No Network", message: "\(networkError.rawValue)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
default:
let alertController = UIAlertController(title: "Error", message: "There is something went wrong. Please try again", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
return
}
self.patientList = patientListPerPayout
return
}
}
APIService.swift
struct Patient: Codable {
let hospitalNumber: Int
let patientName: String
let totalAmount: Double
enum CodingKeys: String, CodingKey {
case hospitalNumber = "hospitalNumber"
case patientName = "patientName"
case totalAmount = "totalAmount"
}
}
struct PatientList {
typealias getPatientListTaskCompletion = (_ patientListperPayout: [Patient]?, _ error: NetworkError?) -> Void
static func getPatientList(doctorNumber: Int, periodId: Int, completion: @escaping getPatientListTaskCompletion) {
guard let patientPerPayoutURL = URL(string: "\(Endpoint.Patient.patientPerPayout)?periodId=\(periodId)&doctorNumber=\(doctorNumber)") else {
completion(nil, .invalidURL)
return
}
let sessionManager = Alamofire.SessionManager.default
sessionManager.session.getAllTasks { (tasks) in
tasks.forEach({ $0.cancel() })
}
Alamofire.request(patientPerPayoutURL, method: .get, encoding: JSONEncoding.default).responseJSON { (response) in
guard HelperMethods.reachability(responseResult: response.result) else {
completion(nil, .noNetwork)
return
}
guard let statusCode = response.response?.statusCode else {
completion(nil, .noStatusCode)
return
}
switch(statusCode) {
case 200:
guard let jsonData = response.data else{
completion(nil, .invalidJSON)
print(statusCode)
return
}
let decoder = JSONDecoder()
do {
let patientListArray = try decoder.decode([Patient].self, from: jsonData)
let sortedPatientListArray = patientListArray.sorted(by: { $0.patientName < $1.patientName })
completion(sortedPatientListArray, nil)
}catch{
completion(nil, .invalidJSON)
print(statusCode)
}
case 400:
completion(nil, .badRequest)
case 404:
completion(nil, .noRecordFound)
default:
print("UNCAPTURED STATUS CODE FROM getPatientList\nSTATUS CODE: \(statusCode)")
completion(nil, .uncapturedStatusCode)
}
}
}
var patientList: [Patient]! {
didSet {
performSegue(withIdentifier: patientListIdentifier, sender: self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.latestCreditedAmountTableView.dataSource = self
self.latestCreditedAmountTableView.delegate = self
configureTableViewCell()
showTotalCreditedAmount()
getDoctorPayoutSummary(doctorNumber: doctorNumber)
}
func getDoctorPayoutSummary(doctorNumber: Int) {
self.payoutSummary = payoutSummaryDetails
self.taxRateVatRateLabel.text = "\(self.payoutSummary.taxRate) / \(self.payoutSummary.vatRate)"
self.getPatientList()
self.latestCreditedAmountTableView.reloadData()
return
}
func getPatientList() {
APIService.PatientList.getPatientList(doctorNumber: doctorNumber, periodId: currentRemittance.periodId) { (patientListArray, error) in
guard let patientListPerPayout = patientListArray, error == nil else {
if let networkError = error {
switch networkError {
case .noRecordFound:
let alertController = UIAlertController(title: "No Record Found", message: "You don't have current payment remittance", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
case .noNetwork:
let alertController = UIAlertController(title: "No Network", message: "\(networkError.rawValue)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
default:
let alertController = UIAlertController(title: "Error", message: "There is something went wrong. Please try again", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
return
}
self.patientList = patientListPerPayout
return
}
}
JSON响应
[
{
"hospitalNumber": null,
"patientName": null,
"totalAmount": 31104
},
{
"hospitalNumber": "",
"patientName": "LastName, FirstName",
"totalAmount": 3439.8
}
]
您的JSON响应显示,一些字段至少可以为null-hospitalNumber
和patientName
。另外,hospitalNumber
是JSON中的一个字符串-感谢@Don指出。您的struct
还应该能够通过使映射字段也可为空来处理这些可为空的问题。即
struct Patient: Codable {
let hospitalNumber: String?
let patientName: String?
let totalAmount: Double
enum CodingKeys: String, CodingKey {
case hospitalNumber = "hospitalNumber"
case patientName = "patientName"
case totalAmount = "totalAmount"
}
}
如果totalAmount
也可以为空,则需要对其执行相同的操作。当然,API在任何情况下返回null是否正确是另一个问题——空医院编号或名称如何有用可能需要解决
确保在使用字段时不会强制展开这些字段。只需在模型类中进行以下更改。将模型类变量定义为可选
,这在API中不是强制性的
struct Patient: Codable {
var hospitalNumber: String?
let patientName: String?
let totalAmount: Double?
enum CodingKeys: String, CodingKey {
case hospitalNumber = "hospitalNumber"
case patientName = "patientName"
case totalAmount = "totalAmount"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let hospitalNumb = try container.decode(Int?.self, forKey: .hospitalNumber) {
hospitalNumber = String(hospitalNumb)
} else {
hospitalNumber = try container.decode(String.self, forKey: .hospitalNumber)
}
patientName = try container.decode(String.self, forKey: .patientName)
totalAmount = try container.decode(Double.self, forKey: .totalAmount)
}
}
注意:
Codable
或Decodable
如果同一个键的类型不同,或者您可以说like该类型与指定的类型不同,则不起作用。能否请您在问题中包含API的JSON响应。@ChrisShaw您好,我编辑了我的问题并包含了JSON响应。感谢您在JSONDecoder
catch块中打印error
实例。我尝试了您的答案,但仍然返回invalidJSON@Titus,这是因为hospitalNumber实际上返回的是一个字符串,虽然是空字符串,但不是Int。另外,如果名称与JSON匹配,则不需要编码键。@我是否将其从Int改为String,在检查使用断点时,提取了struct中变量的值,但遇到错误let sortedPatientListArray=patientListArray.sorted(by:{UIContentSizeCategory(rawValue:$0.patientName!)这是因为您正在强制展开patientName
-但它可以是nil
-如果它是nil
,并且您强制展开它,那么您将得到崩溃。,