Swift解码含嵌套JSON与数组的数据遇阻,求解决方案
解决Swift中解码嵌套JSON数组的问题
首先,我明白你刚接触Swift,在解码这个嵌套结构的JSON时遇到了麻烦——你想把serviceCenter数组转成Decodable对象,但没找对正确的结构映射方式。我们可以用Swift的Codable协议来优雅地解决这个问题,比手动用JSONSerialization解析要简洁得多。
第一步:定义对应JSON结构的Codable结构体
首先我们需要把JSON的层级结构完整映射成Swift结构体:
- 顶层是一个数组,包含两个元素:一个
validation对象,一个result对象 result内部包含serviceCenter数组,每个元素是具体的服务中心信息
先定义最底层的ServiceCenter结构体,注意JSON里的字段类型(比如bDamage是字符串"1",我们可以自定义解码把它转成Bool):
struct ServiceCenter: Codable { let model: String let color: String let make: String let sTag: String let sVin: String let sMiles: String let bDamage: Bool let dDateTime: String // 自定义解码,把"1"/"0"字符串转成Bool init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) model = try container.decode(String.self, forKey: .model) color = try container.decode(String.self, forKey: .color) make = try container.decode(String.self, forKey: .make) sTag = try container.decode(String.self, forKey: .sTag) sVin = try container.decode(String.self, forKey: .sVin) sMiles = try container.decode(String.self, forKey: .sMiles) // 处理bDamage字段 let damageStr = try container.decode(String.self, forKey: .bDamage) bDamage = damageStr == "1" dDateTime = try container.decode(String.self, forKey: .dDateTime) } }
然后定义包含serviceCenter数组的Result结构体:
struct Result: Codable { let serviceCenter: [ServiceCenter] }
最后,我们用枚举来处理顶层数组的两种不同元素(validation和result):
enum ResponseElement: Codable { case validation(Validation) case result(Result) enum CodingKeys: String, CodingKey { case validation, result } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 尝试解码validation if let validation = try container.decodeIfPresent(Validation.self, forKey: .validation) { self = .validation(validation) } // 尝试解码result else if let result = try container.decodeIfPresent(Result.self, forKey: .result) { self = .result(result) } // 都不是的话抛出错误 else { throw DecodingError.dataCorrupted(DecodingError.Context( codingPath: container.codingPath, debugDescription: "无法识别的响应元素类型" )) } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case .validation(let val): try container.encode(val, forKey: .validation) case .result(let res): try container.encode(res, forKey: .result) } } } // 定义Validation结构体(虽然你不需要处理,但解码需要用到) struct Validation: Codable { let bValid: Bool let sDescription: String }
第二步:解码JSON并获取serviceCenter数组
现在可以用JSONDecoder来解码整个JSON,然后提取出serviceCenter数组:
do { // 解码顶层数组 let responseElements = try JSONDecoder().decode([ResponseElement].self, from: data) // 找到数组中的result元素 if let resultElement = responseElements.first(where: { element in if case .result(_) = element { return true } return false }) { // 提取result对象 if case .result(let result) = resultElement { let serviceCenters = result.serviceCenter // 遍历处理每个serviceCenter for sc in serviceCenters { print("品牌:\(sc.make),型号:\(sc.model),颜色:\(sc.color)") // 其他字段可以按需使用 } } } } catch { print("解码失败:\(error.localizedDescription)") print(error) // 打印详细错误信息,方便调试 }
如果你想简化(适合刚入门理解)
如果你觉得枚举有点复杂,也可以结合JSONSerialization和Codable来逐层解析:
do { // 先解码顶层数组 let topLevelArray = try JSONSerialization.jsonObject(with: data) as! [[String: Any]] // 取第二个元素(index为1)里的result字典 guard let resultDict = topLevelArray[1]["result"] as? [String: Any] else { print("找不到result字段") return } // 把result字典转成Data,再解码成Result结构体 let resultData = try JSONSerialization.data(withJSONObject: resultDict) let result = try JSONDecoder().decode(Result.self, from: resultData) // 现在就可以使用serviceCenter数组了 for sc in result.serviceCenter { print(sc) } } catch { print("处理失败:\(error)") }
为什么你之前的Decodable尝试失败?
大概率是因为你没有正确映射整个JSON的层级——serviceCenter不是直接在顶层,而是嵌套在result里,而result又在顶层数组的第二个元素中。直接解码serviceCenter数组的话,解码器找不到对应的层级,所以会失败。
内容的提问来源于stack exchange,提问作者Juan Borja
相关产品推荐
相关产品推荐

