Swift Combine解码JSON仅提取contents数组报Invalid top-level type错排查
崩溃根因
你遇到的Invalid top-level type in JSON write崩溃由两个问题共同导致:
Future闭包内的回调处理逻辑错误:loadJson返回的是Result<Data, Error>类型,你未做分支判断就直接将整个Result枚举作为成功值传给promise,后续JSONSerialization拿到的是Swift枚举值,不属于JSON序列化支持的合法顶层类型(字典/数组/字符串/数字等),直接触发写入崩溃。- 解码目标类型不匹配:接口返回的JSON顶层是对象,对应你定义的
Welcome结构体,你直接指定解码[Content]数组类型,不符合JSON结构。
修复步骤
不需要在模型定义层面做特殊配置,直接调整Combine算子链的逻辑即可,同时补全网络请求的边界判断:
- 修正
fetchShows方法逻辑,删除多余的JSONSerialization中转步骤,直接用接口返回的原始Data解码顶层Welcome结构体,再通过map算子提取contents数组作为最终返回值:
func fetchShows(with query: String) -> AnyPublisher<[Content], Error > { Future<Data, Error> { promise in self.loadJson(withQuery: query) { result in // 分支处理Result类型,不要直接透传result switch result { case .success(let data): promise(.success(data)) case .failure(let error): promise(.failure(error)) } } } // 先解码顶层结构体 .decode(type: Welcome.self, decoder: jsonDecoder) // 提取目标contents数组 .map(\.contents) .eraseToAnyPublisher() }
- 修复
loadJson方法里的回调逻辑漏洞,避免错误场景下重复触发回调,补充空数据异常:
let urlSession = URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in if let error = error { completion(.failure(error)) } else if let data = data { // 加else判断,避免error存在时继续走成功逻辑 completion(.success(data)) } else { completion(.failure(NSError(domain: "APIManager", code: -1, userInfo: [NSLocalizedDescriptionKey: "接口返回空数据"]))) } }
补充说明
- 你当前配置的
keyDecodingStrategy = .convertFromSnakeCase可以正常生效,现有Content、Welcome结构体的属性定义和JSON字段完全匹配,不需要修改。 - 后续遇到类似“接口返回外层包装结构、仅需要内部某字段”的场景,直接在解码完外层结构后用
map算子提取目标字段即可,不需要自定义解码逻辑,维护成本最低。
内容的提问来源于stack exchange,提问作者jat
相关产品推荐
相关产品推荐

