从 Web API 解析 Json
Posted
技术标签:
【中文标题】从 Web API 解析 Json【英文标题】:Parsing Json from Web API 【发布时间】:2022-01-08 08:47:33 【问题描述】:你好,我叫妮可,
我是应用程序编程/SwiftUI 的完全初学者。 我正在尝试从 web api 解析 json 数据,但不知何故我无法正确解析数据。我假设我的 json 结构不正确,但我找不到问题。
我从 Web API 获得的 Json 看起来像这样:
"pois": [
"id": "2635094451",
"lat": "52.410150",
"lat_s": "52.4",
"lng": "10.776630",
"lng_s": "10.8",
"street": "Röntgenstraße",
"content": "8137285512",
"backend": "0-239283152",
"type": "1",
"vmax": "50",
"counter": "0",
"create_date": "2021-11-18 13:21:50",
"confirm_date": "2021-11-18 13:21:43",
"gps_status": "-",
"info": " \"qltyCountryRoad\":1,\"confirmed\":\"0\",\"gesperrt\":\"0\",\"precheck\":\"[Q1|21|0]\"",
"polyline": ""
],
"grid": []
我的结构如下所示:
struct APIResponse: Codable
let pois: [InputDataPois]
let grid: [InputDataGrid]
private enum CodingKeys: String, CodingKey
case pois
case grid
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: CodingKeys.self)
self.pois = try container.decode(APIResponse.self, forKey: .pois).pois
self.grid = try container.decode(APIResponse.self, forKey: .grid).grid
struct InputDataPois: Codable, Identifiable
let id:String
let lat:String
let lat_s:String
let lng:String
let lng_s:String
let street:String
let content:String
let backend:String
let type:String
let vmax:String
let counter:String
let create_date:String
let confirm_date:String
let gps_status:String
let info:String
let polyline:String
extension InputDataPois
private enum CodingKeys: String, CodingKey
case id
case lat
case lat_s
case lng
case lng_s
case street
case content
case backend
case type
case vmax
case counter
case create_date
case confirm_date
case gps_status
case info
case polyline
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.lat = try container.decode(String.self, forKey: .lat)
self.lat_s = try container.decode(String.self, forKey: .lat_s)
self.lng = try container.decode(String.self, forKey: .lng)
self.lng_s = try container.decode(String.self, forKey: .lng_s)
self.street = try container.decode(String.self, forKey: .street)
self.content = try container.decode(String.self, forKey: .content)
self.backend = try container.decode(String.self, forKey: .backend)
self.type = try container.decode(String.self, forKey: .type)
self.vmax = try container.decode(String.self, forKey: .vmax)
self.counter = try container.decode(String.self, forKey: .counter)
self.create_date = try container.decode(String.self, forKey: .create_date)
self.confirm_date = try container.decode(String.self, forKey: .confirm_date)
self.gps_status = try container.decode(String.self, forKey: .gps_status)
self.info = try container.decode(String.self, forKey: .info)
self.polyline = try container.decode(String.self, forKey: .polyline)
struct InputDataGrid: Codable
我的捆绑包是这样的:
extension Bundle
func decode(_ file: String) -> [InputDataPois]
// 1. Locate the Json File
guard let url = URL(string: file) else
fatalError("Failed to locate \(file) in bundle")
// 2.Create a property for the Data
guard let data = try? Data(contentsOf: url) else
fatalError("Failed to Load \(file) from bundle.")
// 3. Create a property for the data
let str = String(decoding: data, as: UTF8.self)
print("\(str)")
guard let loaded = try? JSONDecoder().decode(APIResponse.self, from: data).pois else
fatalError("Failed to decode \(file) from bundle.")
// 4. Return the ready-to-use data
return loaded
在我看来,我正在使用:
let speed: [InputDataPois] = Bundle.main.decode("https://cdn2.atudo.net/api/1.0/vl.php?type=0,1,2,3,4,5,6&box=52.36176390234046,10.588760375976562,52.466468685912744,11.159706115722656")
我得到的错误看起来像这样。
ErrorMessage ConsoleError
提前感谢您的帮助。
【问题讨论】:
您拥有的解码代码是用于解码应用资源文件的内容,而不是从外部API下载和解码。查找使用 URLSession 的教程/文章,以避免您现在拥有的同步代码。至于解码错误,您需要打印实际的错误消息,因此您应该使用try
和do/catch
而不是try?
。
我把你上面的 JSON 转储到app.quicktype.io。忽略所有“助手”功能并提供一个空的InputDataGrid
struct
,我能够毫无问题地解码发布的 JSON 和 URL 响应
【参考方案1】:
删除您的自定义 Codable 实现和编码键。它们不是必需的,编译器可以使用这个简单的 JSON 为您生成它们。那么一切都应该正常。
这里的问题具体是你的 APIResponse 解码:
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: CodingKeys.self)
self.pois = try container.decode(APIResponse.self, forKey: .pois).pois
self.grid = try container.decode(APIResponse.self, forKey: .grid).grid
要解码一个 APIResponse,您尝试解码两个 APIResponse。这将再次尝试解码 APIResponse 导致无限递归。您没有看到的唯一原因是您的 JSON 包含数组,导致第二次调用 container(keyedBy:)
失败。尝试在 init(from:)
的第一行设置断点(或者如果您必须使用 print 语句),您会看到它在失败之前被第二次调用。
要解决您需要解码属性的实际类型的问题:
self.pois = try container.decode([InputDataPois].self, forKey: .pois)
但正如我所说,你的 JSON 格式没有什么需要手动实现解码器,所以最好是让编译器为你合成它。
【讨论】:
感谢您的快速响应,我尝试了它,现在它正在工作,感谢您的帮助。以上是关于从 Web API 解析 Json的主要内容,如果未能解决你的问题,请参考以下文章
Angular 2/Web Api - json 解析错误语法错误意外结束输入