Swift解析JSON获取formatted_address时崩溃,寻求解决方案
解决Swift解析JSON时results字段类型不匹配的崩溃问题
我来帮你搞定这个问题!你的崩溃原因很明确:你把JSON里的results数组错误地强制转换成了字典类型[String:Any],而实际上results是一个包含字典元素的数组(哪怕只有一个元素,它的类型依然是数组,也就是你看到的__NSSingleObjectArrayI——这是OC底层的单元素数组类型,对应Swift里的Array)。
问题分析
你初始代码打印的JSON里,results是一个数组结构,比如类似这样的格式:
{ "results": [ { "formatted_address": "xxx", "address_components": [...] } ] }
所以json["results"]的真实类型是[[String:Any]](字典数组),而你用as! [String:Any]强行转成单个字典,类型不匹配,自然会触发运行时崩溃。
修正后的代码(安全解析版)
我们用可选绑定替代强制解包,既避免崩溃,又能正确获取目标字段:
func loadUrl(url: String) { DispatchQueue.global().async { do { // 先检查URL是否有效,避免强制解包URL的崩溃 guard let appUrl = URL(string: url) else { DispatchQueue.main.async { print("无效的URL地址") } return } let data = try Data(contentsOf: appUrl) let json = try JSONSerialization.jsonObject(with: data) as! [String: Any] // 正确将results解析为字典数组 if let results = json["results"] as? [[String: Any]] { // 取出数组中的第一个结果(如果数组非空) if let firstResult = results.first { // 获取formatted_address字段 if let formattedAddress = firstResult["formatted_address"] as? String { DispatchQueue.main.async { print("格式化地址:\(formattedAddress)") } } // 获取address_components字段(它本身也是一个数组) if let addressComponents = firstResult["address_components"] as? [[String: Any]] { DispatchQueue.main.async { print("地址组件:\(addressComponents)") } } } else { DispatchQueue.main.async { print("results数组为空") } } } else { DispatchQueue.main.async { print("无法解析results字段") } } } catch { DispatchQueue.main.async { print("请求或解析错误:\(error.localizedDescription)") } } } }
更优雅的方案:使用Codable协议
如果你的项目经常需要解析JSON,强烈推荐使用Swift的Codable协议,它比JSONSerialization更类型安全,代码也更简洁,能从根源上避免类型转换错误:
首先定义对应JSON结构的模型:
// 地址组件模型 struct AddressComponent: Codable { let long_name: String let short_name: String let types: [String] } // 单个地址结果模型 struct AddressResult: Codable { let formatted_address: String let address_components: [AddressComponent] } // 整体响应模型 struct GeocodeResponse: Codable { let results: [AddressResult] }
然后用JSONDecoder解码:
func loadUrlWithCodable(url: String) { DispatchQueue.global().async { do { guard let appUrl = URL(string: url) else { DispatchQueue.main.async { print("无效的URL地址") } return } let data = try Data(contentsOf: appUrl) // 直接解码为模型对象,编译器会帮我们检查类型 let response = try JSONDecoder().decode(GeocodeResponse.self, from: data) if let firstResult = response.results.first { DispatchQueue.main.async { print("格式化地址:\(firstResult.formatted_address)") print("地址组件:\(firstResult.address_components)") } } else { DispatchQueue.main.async { print("没有找到地址结果") } } } catch { DispatchQueue.main.async { print("请求或解析错误:\(error.localizedDescription)") } } } }
这种方式不需要手动做类型转换,编译器会在编译阶段检查字段是否匹配,大大降低运行时崩溃的概率。
内容的提问来源于stack exchange,提问作者Patrick Haertel
相关产品推荐
相关产品推荐

