当我运行代码时,出现错误 ---------------------- “[NSJSONSerialization dataWithJSONObject:options:error:]: Invalid top-level type in JSON 写入”。 ----------------------
class BaseAPI<T: TargetType> {
func fetchData<M: Decodable>(target: T, responseClass: M.Type, completion:@escaping(Result<M?, NSError>) -> Void) {
let method = Alamofire.HTTPMethod(rawValue: target.methods.rawValue)
let headers = Alamofire.HTTPHeaders(target.headers ?? [:])
let params = buildParams(task: target.task)
AF.request(target.baseUrl + target.path, method: method, parameters: params.0, encoding: params.1, headers: headers).responseDecodable(of: M.self) { response in
guard let statusCode = response.response?.statusCode else {
//ADD Custom Error
completion(.failure(NSError()))
return
}
if statusCode == 200 {
// Successful Request
guard let jsonResponse = try? response.result.get() else {
completion(.failure(NSError()))
return
}
guard let theJSONData = try? JSONSerialization.data(withJSONObject: jsonResponse, options: []) else {
completion(.failure(NSError()))
return
}
guard let responseObj = try? JSONDecoder().decode(M.self, from: theJSONData) else {
completion(.failure(NSError()))
return
}
completion(.success(responseObj))
} else {
completion(.failure(NSError()))
}
}
}
private func buildParams(task: Task) -> ([String:Any], ParameterEncoding) {
switch task {
case .requestPlain:
return ([:], URLEncoding.default)
case .requestParameters(parameters: let parameters, encoding: let encoding):
return (parameters, encoding)
}
}
}
而不是
try? JSONSerialization.data(withJSONObject: jsonResponse, options: [])
当我尝试 try? JSONSerialization.data(withJSONObject: jsonResponse, options: [])
代码时,它给出错误 No exact matches in call to class method 'jsonObject'
。
回答你的问题,
jsonResponse
已经是解码的对象,M
。无法使用任意对象调用 JSONSerialization
方法 data(withJSONObject:options:)
。它仅适用于非常特定的类型。 (请参阅 JSONSerialization
文档中可接受的类型列表。)这就是您收到此错误的原因。
话虽如此,这就引出了一个问题,为什么有人会尝试用
Data
将对象重新编码回 JSONSerialization
,并用 Data
将 JSONDecoder
重新解码回 M
。我建议消除这个,将代码简化为:
func fetchData<M: Decodable>(target: T, responseClass: M.Type = M.self, completion: @escaping(Result<M, Error>) -> Void) {
let method = Alamofire.HTTPMethod(rawValue: target.methods.rawValue)
let headers = Alamofire.HTTPHeaders(target.headers ?? [:])
let params = buildParams(task: target.task)
AF.request(target.baseUrl + target.path, method: method, parameters: params.0, encoding: params.1, headers: headers)
.validate()
.responseDecodable(of: M.self) { response in
switch response.result {
case .success(let value): completion(.success(value))
case .failure(let error): completion(.failure(error))
}
}
}
一些观察:
作为一个小的改进,我建议将
responseClass
作为可选参数(通常可以从上下文推断出类型)。但我保留了编译器无法自动推断类型的情况下的参数)。
我还使用
validate
来检查状态代码。您不需要自己手动检查。
另请注意,在简化过程中,我已经不再使用
try?
。 try?
是一种反模式,因为它会丢弃有意义的诊断信息。
始终传递有意义的错误。如果调用者不关心详细信息,它可以根据需要忽略错误的详细信息。但在您确实存在解析错误的情况下,了解详细信息非常有用(这样您就知道哪个字段导致了问题)。