JSON解析遇序列化错误:无法用字符串下标访问AudiobookJSON类型
你遇到的错误提示很明确:
Error 1: Cannot subscript a value of type 'AudiobookJSON' (aka
Array<Dictionary<String, Any>>) with an index of type 'String'
简单说,你把数组类型的AudiobookJSON当成了单个字典来用字符串下标(比如dictionary["id"])访问,但数组只能用整数下标(比如array[0]),这就是问题的核心。
下面给你两种实用的修复方案,根据你的需求选择:
方案一:用JSONDecoder自动解析(推荐,Swift最佳实践)
既然你已经让Audiobook实现了Codable协议,完全没必要手动写初始化方法来解析字典——JSONDecoder会自动帮你处理所有映射工作,代码更简洁也更安全。
1. 简化Audiobook模型
删掉自定义的init(dictionary:)方法,只保留Codable属性定义即可:
import Foundation struct Audiobook : Codable { let id: Int let descricao: String let urlImagem: String }
2. 适配API的JSON结构
我注意到你注释掉了一段解析json["data"]的代码,说明这个API的返回结构可能是外层包含data字段的对象,而不是直接返回数组。如果是这样,你需要新增一个结构体来匹配外层的JSON结构:
// 新增一个结构体匹配API返回的外层结构 struct AudiobookResponse: Codable { let data: [Audiobook] }
然后修改APIClient里的解析代码:
do { let response = try JSONDecoder().decode(AudiobookResponse.self, from: unwrappedDAta) let posts = response.data print(posts) completion(posts) } catch { print("Could not get API data. \(error), \(error.localizedDescription)") }
如果API确实直接返回数组,那你原来的JSONDecoder().decode([Audiobook].self, from: unwrappedDAta)代码是没问题的,只要保留简化后的Audiobook模型就行。
方案二:手动解析字典数组(如果需要手动控制解析逻辑)
如果你不想用Codable,坚持手动处理字典,那需要调整类型定义和初始化逻辑:
1. 修正Audiobook的初始化方法
把初始化方法的参数类型从数组AudiobookJSON改成单个字典[String: Any],同时替换强制解包为更安全的可选绑定+默认值:
import Foundation struct Audiobook { let id: Int let descricao: String let urlImagem: String init(dictionary: [String: Any]) { // 用可选绑定+默认值替代强制解包,避免字段缺失时崩溃 self.id = dictionary["id"] as? Int ?? 0 self.descricao = dictionary["descricao"] as? String ?? "" self.urlImagem = dictionary["urlImagem"] as? String ?? "" } }
2. 调整APIClient的解析逻辑
用JSONSerialization解析出字典数组,然后遍历数组逐个创建Audiobook实例:
import Foundation typealias AudiobookJSON = [[String: Any]] struct APIClient { static func getAudiobooksAPI(completion: @escaping ([Audiobook]?) -> Void) { let url = URL(string: "https://alodjinha.herokuapp.com/categoria") let session = URLSession.shared guard let unwrappedURL = url else { print("Error unwrapping URL"); return } let dataTask = session.dataTask(with: unwrappedURL) { (data, response, error) in guard let unwrappedData = data else { print("Error unwrapping data"); return } do { // 先解析成字典数组 guard let postsJSON = try JSONSerialization.jsonObject(with: unwrappedData, options: .allowFragments) as? AudiobookJSON else { print("Failed to cast response to AudiobookJSON") completion(nil) return } // 遍历数组创建Audiobook实例 let posts = postsJSON.map { Audiobook(dictionary: $0) } print(posts) completion(posts) } catch { print("Could not get API data. \(error), \(error.localizedDescription)") } } dataTask.resume() } }
内容的提问来源于stack exchange,提问作者Bruno Vavretchek

