使用泛型和 Decodable 解码 JSON 数组
Decode a JSON array using generics and Decodable
我正在尝试制作一个应用程序以学习如何在 Swift 中使用泛型。所以我决定使用 Studio Ghibli's API
为此,我执行以下操作:
- 创建对 API
的请求
- 获取响应
- 解析 JSON
与他们的 API 一样,所有信息都带有一个 baseURL
等于 https://ghibliapi.herokuapp.com
并且所有端点都是 baseURL/foo
和 return 数组我们想要的任何信息。因此,对于这种特殊情况,我尝试使用 /films
和 /people
.
因此,在我看来,最好有一个通用的解码函数来执行此操作,并且 returns 是我们要解码的相应 class 的数组。
private func decode<T: Decodable>(json: Data, as clazz: T) {
do {
let decoder = JSONDecoder()
let films = try decoder.decode([T].self, from: json)
} catch {
print("An error occurred while parsing JSON")
}
}
我称之为:
self.decode(json: safeData, as: Films.self) //Error thrown here
我的Films
class如下:
import Foundation
import CoreData
@objc(Films)
public class Films: NSManagedObject, Decodable {
enum CodingKeys: String, CodingKey {
case id
case title
case film_description = "description"
case director
case producer
case release_date
case rt_score
}
required public init(from decoder: Decoder) throws {
guard let context = CodingUserInfoKey(rawValue: "context"),
let managedObjectContext = decoder.userInfo[context] as? NSManagedObjectContext,
let entity = NSEntityDescription.entity(forEntityName: "Films", in: managedObjectContext) else {
fatalError("Failed to decode Films!")
}
super.init(entity: entity, insertInto: nil)
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
id = try container.decodeIfPresent(String.self, forKey: .id)
title = try container.decodeIfPresent(String.self, forKey: .title)
film_description = try container.decodeIfPresent(String.self, forKey: .film_description)
director = try container.decodeIfPresent(String.self, forKey: .director)
producer = try container.decodeIfPresent(String.self, forKey: .producer)
release_date = try container.decodeIfPresent(String.self, forKey: .release_date)
rt_score = try container.decodeIfPresent(String.self, forKey: .rt_score)
} catch let error {
print(error)
throw error
}
}
}
但我一直收到错误消息:
Argument type 'Films.Type' does not conform to expected type 'Decodable'
我在 Swift 论坛中阅读了这些主题:
- Passing decodable object as generic parameter 这帮助我将
Decodable
放在函数 中
有些人说这些解决方案对他们有用,但它与我的概念相同,我仍然无法弄清楚如何解决它。
正确的做法是什么?
解决方法很简单:
这个函数:
private func decode<T: Decodable>(json: Data, as clazz: T) {
变成:
private func decode<T: Decodable>(json: Data, as clazz: T.Type) {
就是这样!这就是所有需要完成的事情
我正在尝试制作一个应用程序以学习如何在 Swift 中使用泛型。所以我决定使用 Studio Ghibli's API
为此,我执行以下操作:
- 创建对 API 的请求
- 获取响应
- 解析 JSON
与他们的 API 一样,所有信息都带有一个 baseURL
等于 https://ghibliapi.herokuapp.com
并且所有端点都是 baseURL/foo
和 return 数组我们想要的任何信息。因此,对于这种特殊情况,我尝试使用 /films
和 /people
.
因此,在我看来,最好有一个通用的解码函数来执行此操作,并且 returns 是我们要解码的相应 class 的数组。
private func decode<T: Decodable>(json: Data, as clazz: T) {
do {
let decoder = JSONDecoder()
let films = try decoder.decode([T].self, from: json)
} catch {
print("An error occurred while parsing JSON")
}
}
我称之为:
self.decode(json: safeData, as: Films.self) //Error thrown here
我的Films
class如下:
import Foundation
import CoreData
@objc(Films)
public class Films: NSManagedObject, Decodable {
enum CodingKeys: String, CodingKey {
case id
case title
case film_description = "description"
case director
case producer
case release_date
case rt_score
}
required public init(from decoder: Decoder) throws {
guard let context = CodingUserInfoKey(rawValue: "context"),
let managedObjectContext = decoder.userInfo[context] as? NSManagedObjectContext,
let entity = NSEntityDescription.entity(forEntityName: "Films", in: managedObjectContext) else {
fatalError("Failed to decode Films!")
}
super.init(entity: entity, insertInto: nil)
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
id = try container.decodeIfPresent(String.self, forKey: .id)
title = try container.decodeIfPresent(String.self, forKey: .title)
film_description = try container.decodeIfPresent(String.self, forKey: .film_description)
director = try container.decodeIfPresent(String.self, forKey: .director)
producer = try container.decodeIfPresent(String.self, forKey: .producer)
release_date = try container.decodeIfPresent(String.self, forKey: .release_date)
rt_score = try container.decodeIfPresent(String.self, forKey: .rt_score)
} catch let error {
print(error)
throw error
}
}
}
但我一直收到错误消息:
Argument type 'Films.Type' does not conform to expected type 'Decodable'
我在 Swift 论坛中阅读了这些主题:
- Passing decodable object as generic parameter 这帮助我将
Decodable
放在函数 中
有些人说这些解决方案对他们有用,但它与我的概念相同,我仍然无法弄清楚如何解决它。
正确的做法是什么?
解决方法很简单:
这个函数:
private func decode<T: Decodable>(json: Data, as clazz: T) {
变成:
private func decode<T: Decodable>(json: Data, as clazz: T.Type) {
就是这样!这就是所有需要完成的事情