Swift 4解析嵌套JSON报错:圣经JSON转模型遇字段识别问题
首先,你的核心问题在于模型定义和JSON结构不匹配,再加上错误处理太宽松(用try?吞掉了所有解码错误),导致你看不到真正的问题根源,误以为字段不存在。我帮你一步步梳理问题和修复方案:
一、Verse结构体的定义不符合JSON结构
你的JSON里每个经文对象是一个只有单个键值对的字典(比如{"1":"In the beginning God created the heaven and the earth."}),但你现在定义的Verse是把整个字典塞进verse属性里,这不仅不方便后续访问章节号和经文内容,还会导致解码逻辑混乱。
正确的做法是自定义Verse的解码逻辑,把动态的数字键作为number属性,对应的字符串值作为text属性:
struct Verse: Codable { let number: String let text: String // 自定义解码逻辑,处理动态键 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: DynamicCodingKey.self) // 每个经文对象只有一个键值对,直接取第一个键 guard let key = container.allKeys.first else { throw DecodingError.dataCorrupted(.init( codingPath: decoder.codingPath, debugDescription: "经文对象没有找到有效键" )) } number = key.stringValue text = try container.decode(String.self, forKey: key) } // 用于处理动态字符串键的CodingKey private struct DynamicCodingKey: CodingKey { let stringValue: String init?(stringValue: String) { self.stringValue = stringValue } var intValue: Int? { nil } init?(intValue: Int) { nil } } }
二、错误处理太宽松,导致无法定位问题
你在解析时用了try?,这会把所有解码错误(比如字段不匹配、类型错误)都转换成nil,你完全看不到哪里出了问题。换成do-catch捕获错误,就能直接看到具体的错误信息:
func parse(json: Data) { let decoder = JSONDecoder() do { let jsonBible = try decoder.decode(Bible.self, from: json) bible = jsonBible // 安全访问book属性,避免强制解包崩溃 myText = bible?.book print(myText ?? "获取书名失败") // 测试访问章节和经文 if let firstChapter = bible?.chapters.first { print("第一章:\(firstChapter.chapter)") if let firstVerse = firstChapter.verses.first { print("第\(firstVerse.number)节:\(firstVerse.text)") } } } catch { print("解码错误详情:\(error)") } }
三、移除多余的无效代码
你在parse函数里写了这两行完全多余的代码:
bible = jsonBible.self bible?.book = jsonBible.book
jsonBible已经是完整的Bible实例,直接bible = jsonBible就足够了,后面的赋值操作没有任何意义,只会造成混淆。
四、避免强制解包的崩溃风险
你原来的myText = bible!.book用了强制解包!,如果解码失败bible是nil,这行代码会直接导致APP崩溃。改用可选访问或者空合运算符??来处理,保证代码的安全性。
额外优化:不要在主线程做同步网络请求
你用Data(contentsOf: url)是同步网络请求,会阻塞主线程,导致UI卡顿甚至被系统终止。建议换成URLSession的异步请求:
override func viewDidLoad() { super.viewDidLoad() let urlString = "https://raw.githubusercontent.com/aruljohn/Bible-kjv/master/Genesis.json" guard let url = URL(string: urlString) else { print("无效的URL地址") return } // 异步网络请求 URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let data = data, error == nil else { print("网络请求失败:\(error?.localizedDescription ?? "未知错误")") return } // 回到主线程处理解析结果 DispatchQueue.main.async { self?.parse(json: data) } }.resume() }
做完这些修改后,你的模型就能正确解析JSON数据,也能正常访问book、chapters以及每一节经文的内容了。
内容的提问来源于stack exchange,提问作者wowmystiik

