1

尝试在解码 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()
    }

我相信答案对某些人来说是很明显的,但我一直在用头撞墙。谢谢。

4

1 回答 1

1

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:)包括有关解析问题的含义信息。我建议捕获/显示那个原始错误,因为它会告诉你它失败的确切原因。在块中打印错误消息catch,或者将原始错误作为关联值包含在invalidData错误中。但就目前而言,您正在丢弃decode(_:from:).


不相关,但您可能会更改Event为使用URLDate类型:

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 代码遵循 camelCase 命名约定,即使 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"
    }
}
于 2022-01-20T20:41:04.370 回答