Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/110.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 在Alamofire中使用get请求时,如何修复swift中无效的JSON响应?_Ios_Swift_Rest_Api_Alamofire - Fatal编程技术网

Ios 在Alamofire中使用get请求时,如何修复swift中无效的JSON响应?

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

我正在创建一个应用程序,其中它从API服务器中提取
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
,并且您强制展开它,那么您将得到崩溃。,