Swift中JSONDecoder解码API返回JSON响应失败问题排查
SwiftUI 解码JSON失败排查及修复
你的解码失败是多个字段不匹配、类型错误、解码策略配置错误共同导致的,具体问题点如下:
- 字段拼写错误:
Project结构体中字段写为alternativeCode,但JSON对应字段为alternativeCodes(复数,末尾带s)AssignedProject结构体中字段写为emloyeeId(拼写错误,少了字母p),JSON对应字段为employeeId
- 字段类型不匹配:
- JSON中
employeeId的值是字符串类型"13",你定义为Int类型,类型不匹配无法解码 - JSON中
until字段值为null,你定义为非可选的Date类型,遇到空值直接解码失败,需要声明为可选类型Date?
- JSON中
- 日期解码策略错误:
- 你配置的
.iso8601解码策略仅支持带时间、时区的完整ISO8601格式字符串(例如2016-01-01T00:00:00Z),但接口返回的日期是纯yyyy-MM-dd格式的短日期字符串,无法被默认策略解析,需要自定义日期格式化器。
- 你配置的
- 额外逻辑问题:
- 你已经在
Task异步上下文里调用请求方法,不需要混用老式的dataTask回调写法,用Swift原生的async/await网络接口更简洁,也不会出现作用域引用错误。 - 当前
doHTTPProjectsCall方法返回的是硬编码的字符串数组,和你要解码的项目数据完全无关,也没有把解码结果正确回传给视图状态。
- 你已经在
修正后的可运行代码
import SwiftUI // 修正字段名、类型、可选值配置 struct Project: Decodable { let id: String let version: Int let name: String let code: String let start: Date let end: Date let alternativeCodes: [String] // 修正拼写为复数 } struct AssignedProject: Decodable{ let from: Date let until: Date? // 修正为可选类型,适配null值 let employeeId: String // 修正拼写、类型为String匹配JSON let project: Project } struct FetchWebsiteView: View { @Binding var apiKey : String @State var assignedProjects = [AssignedProject]() // 自定义日期格式化器,适配yyyy-MM-dd格式 private let projectDecoder: JSONDecoder = { let decoder = JSONDecoder() let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" dateFormatter.locale = Locale(identifier: "en_US_POSIX") dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) decoder.dateDecodingStrategy = .formatted(dateFormatter) return decoder }() var body: some View { VStack{ // 这里可以编写项目列表渲染逻辑 List(assignedProjects, id: \.from) { item in Text(item.project.name) } } .onAppear{ Task { do { assignedProjects = try await fetchAssignedProjects(token: apiKey) } catch { print("解码或请求错误: \(error)") // 打印具体错误方便排查 } } } } // 改用async/await写法,避免回调地狱,直接返回解码后的数据 func fetchAssignedProjects(token:String) async throws -> [AssignedProject] { let url = "http://localhost:8160/api/v1/project/assignments/13" guard let reqUrl = URL(string: url) else { throw URLError(.badURL) } var req = URLRequest(url: reqUrl) req.httpMethod = "GET" req.setValue("CC0001", forHTTPHeaderField: "CC-Tenant") req.setValue("BE", forHTTPHeaderField: "CC-Product") // 原代码未传入token做鉴权,可根据接口要求补充请求头 let (data, response) = try await URLSession.shared.data(for: req) guard let httpResp = response as? HTTPURLResponse, httpResp.statusCode == 200 else { throw URLError(.badServerResponse) } // 不要用try?吞掉错误,直接抛出可快速定位解码失败的具体位置 return try projectDecoder.decode([AssignedProject].self, from: data) } }
调试提示:解码阶段不要用
try?屏蔽错误,直接用do/catch捕获并打印error,可以直接看到具体是哪个字段、哪个位置解码失败,比盲查效率高很多。
内容的提问来源于stack exchange,提问作者Pjaks
相关产品推荐
相关产品推荐

