无法使用 Swift 解码 JSON 数据
Trouble Decoding JSON Data with Swift
尝试在解码 JSON 数据方面进行一些练习,但我遇到了问题。我知道 URL 是有效的,但出于某种原因,我的解码器不断抛出错误。下面是我的模型结构、我要解码的 JSON 对象和我的解码器。
模型结构:
struct Event: Identifiable, Decodable {
let id: Int
let description: String
let title: String
let timestamp: String
let image: String
let phone: String
let date: String
let locationline1: String
let locationline2: String
}
struct EventResponse: Decodable {
let request: [Event]
}
JSON 响应:
[
{
"id": 1,
"description": "Rebel Forces spotted on Hoth. Quell their rebellion for the Empire.",
"title": "Stop Rebel Forces",
"timestamp": "2015-06-18T17:02:02.614Z",
"image": "https://raw.githubusercontent.com/phunware-services/dev-interview-homework/master/Images/Battle_of_Hoth.jpg",
"date": "2015-06-18T23:30:00.000Z",
"locationline1": "Hoth",
"locationline2": "Anoat System"
},
{
"id": 2,
"description": "All force-sensitive members of the Empire must report to the Sith Academy on Korriban. Test your passion, attain power, to defeat your enemy on the way to becoming a Dark Lord of the Sith",
"title": "Sith Academy Orientation",
"timestamp": "2015-06-18T21:52:42.865Z",
"image": "https://raw.githubusercontent.com/phunware-services/dev-interview-homework/master/Images/Korriban_Valley_TOR.jpg",
"phone": "1 (800) 545-5334",
"date": "2015-09-27T15:00:00.000Z",
"locationline1": "Korriban",
"locationline2": "Horuset System"
},
{
"id": 3,
"description": "There is trade dispute between the Trade Federation and the outlying systems of the Galactic Republic, which has led to a blockade of the small planet of Naboo. You must smuggle supplies and rations to citizens of Naboo through the blockade of Trade Federation Battleships",
"title": "Run the Naboo Blockade",
"timestamp": "2015-06-26T03:50:54.161Z",
"image": "https://raw.githubusercontent.com/phunware-services/dev-interview-homework/master/Images/Blockade.jpg",
"phone": "1 (949) 172-0789",
"date": "2015-07-12T19:08:00.000Z",
"locationline1": "Naboo",
"locationline2": "Naboo System"
}
]
我的解码器:
func getEvents(completed: @escaping (Result<[Event], APError>) -> Void) {
guard let url = URL(string: eventURL) else {
completed(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: URLRequest(url: url)) { data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
let decodedResponse = try decoder.decode(EventResponse.self, from: data)
completed(.success(decodedResponse.request))
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
}
我相信答案对某些人来说是显而易见的,但我一直在用头撞墙。谢谢。
EventResponse
建议 JSON 的形式为:
{
"request": [...]
}
但这显然不是您的 JSON 包含的内容。
但是你可以替换:
let decodedResponse = try decoder.decode(EventResponse.self, from: data)
有:
let decodedResponse = try decoder.decode([Event].self, from: data)
并且不再需要 EventResponse
类型。
FWIW,在 catch
块中,您返回了 .invalidData
错误。但是 decode(_:from:)
抛出的错误包含有关解析问题的含义信息。我会建议 capturing/displaying 那个原始错误,因为它会告诉你失败的确切原因。要么在 catch
块中打印错误消息,要么将原始错误作为 associated value 包含在 invalidData
错误中。但就目前而言,您正在丢弃 decode(_:from:)
.
抛出的错误中包含的所有有用信息
无关,但您可以将 Event
更改为使用 URL
和 Date
类型:
struct Event: Identifiable, Decodable {
let id: Int
let description: String
let title: String
let timestamp: Date
let image: URL
let phone: String
let date: Date
let locationline1: String
let locationline2: String
}
并配置您的日期格式以便为您解析这些日期:
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0) // not necessary because you have timezone in the date string, but useful if you ever use this formatter with `JSONEncoder`
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSX"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
如果你想让你的 Swift 代码遵循驼峰式命名约定,即使 API 没有,你可以手动指定你的编码键:
struct Event: Identifiable, Decodable {
let id: Int
let description: String
let title: String
let timestamp: Date
let image: URL
let phone: String
let date: Date
let locationLine1: String
let locationLine2: String
enum CodingKeys: String, CodingKey {
case id, description, title, timestamp, image, phone, date
case locationLine1 = "locationline1"
case locationLine2 = "locationline2"
}
}
尝试在解码 JSON 数据方面进行一些练习,但我遇到了问题。我知道 URL 是有效的,但出于某种原因,我的解码器不断抛出错误。下面是我的模型结构、我要解码的 JSON 对象和我的解码器。
模型结构:
struct Event: Identifiable, Decodable {
let id: Int
let description: String
let title: String
let timestamp: String
let image: String
let phone: String
let date: String
let locationline1: String
let locationline2: String
}
struct EventResponse: Decodable {
let request: [Event]
}
JSON 响应:
[
{
"id": 1,
"description": "Rebel Forces spotted on Hoth. Quell their rebellion for the Empire.",
"title": "Stop Rebel Forces",
"timestamp": "2015-06-18T17:02:02.614Z",
"image": "https://raw.githubusercontent.com/phunware-services/dev-interview-homework/master/Images/Battle_of_Hoth.jpg",
"date": "2015-06-18T23:30:00.000Z",
"locationline1": "Hoth",
"locationline2": "Anoat System"
},
{
"id": 2,
"description": "All force-sensitive members of the Empire must report to the Sith Academy on Korriban. Test your passion, attain power, to defeat your enemy on the way to becoming a Dark Lord of the Sith",
"title": "Sith Academy Orientation",
"timestamp": "2015-06-18T21:52:42.865Z",
"image": "https://raw.githubusercontent.com/phunware-services/dev-interview-homework/master/Images/Korriban_Valley_TOR.jpg",
"phone": "1 (800) 545-5334",
"date": "2015-09-27T15:00:00.000Z",
"locationline1": "Korriban",
"locationline2": "Horuset System"
},
{
"id": 3,
"description": "There is trade dispute between the Trade Federation and the outlying systems of the Galactic Republic, which has led to a blockade of the small planet of Naboo. You must smuggle supplies and rations to citizens of Naboo through the blockade of Trade Federation Battleships",
"title": "Run the Naboo Blockade",
"timestamp": "2015-06-26T03:50:54.161Z",
"image": "https://raw.githubusercontent.com/phunware-services/dev-interview-homework/master/Images/Blockade.jpg",
"phone": "1 (949) 172-0789",
"date": "2015-07-12T19:08:00.000Z",
"locationline1": "Naboo",
"locationline2": "Naboo System"
}
]
我的解码器:
func getEvents(completed: @escaping (Result<[Event], APError>) -> Void) {
guard let url = URL(string: eventURL) else {
completed(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: URLRequest(url: url)) { data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
let decodedResponse = try decoder.decode(EventResponse.self, from: data)
completed(.success(decodedResponse.request))
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
}
我相信答案对某些人来说是显而易见的,但我一直在用头撞墙。谢谢。
EventResponse
建议 JSON 的形式为:
{
"request": [...]
}
但这显然不是您的 JSON 包含的内容。
但是你可以替换:
let decodedResponse = try decoder.decode(EventResponse.self, from: data)
有:
let decodedResponse = try decoder.decode([Event].self, from: data)
并且不再需要 EventResponse
类型。
FWIW,在 catch
块中,您返回了 .invalidData
错误。但是 decode(_:from:)
抛出的错误包含有关解析问题的含义信息。我会建议 capturing/displaying 那个原始错误,因为它会告诉你失败的确切原因。要么在 catch
块中打印错误消息,要么将原始错误作为 associated value 包含在 invalidData
错误中。但就目前而言,您正在丢弃 decode(_:from:)
.
无关,但您可以将 Event
更改为使用 URL
和 Date
类型:
struct Event: Identifiable, Decodable {
let id: Int
let description: String
let title: String
let timestamp: Date
let image: URL
let phone: String
let date: Date
let locationline1: String
let locationline2: String
}
并配置您的日期格式以便为您解析这些日期:
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0) // not necessary because you have timezone in the date string, but useful if you ever use this formatter with `JSONEncoder`
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSX"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
如果你想让你的 Swift 代码遵循驼峰式命名约定,即使 API 没有,你可以手动指定你的编码键:
struct Event: Identifiable, Decodable {
let id: Int
let description: String
let title: String
let timestamp: Date
let image: URL
let phone: String
let date: Date
let locationLine1: String
let locationLine2: String
enum CodingKeys: String, CodingKey {
case id, description, title, timestamp, image, phone, date
case locationLine1 = "locationline1"
case locationLine2 = "locationline2"
}
}