You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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?
  • 日期解码策略错误:
    • 你配置的.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:24:23