我是编写 swift 的新手,因此欢迎任何有关改进/最佳实践的提示,但我的主要问题是我在访问嵌套 JSON 数组列表时遇到问题。 我正在使用这个免费的 API 并尝试显示字符列表https://swapi.dev/api/people/
请参阅下面的代码片段。
当我打印类型时,它的 :Optional<Any>
当我打印json["results"]
时,它会打印数组,如下所示:
Optional(<__NSArrayI 0x600000fe31e0>(
{
"birth_year" = 19BBY;
created = "2014-12-09T13:50:51.644000Z";
....
我尝试了几种不同的事情,但没有成功。 有人可以就如何在json["results"
下迭代列表提供一些建议吗?
func onLoad() -> Void {
let url = URL(string: "https://swapi.dev/api/people")
guard let requestUrl = url else { fatalError() }
// Create URL Request
var request = URLRequest(url: requestUrl)
// Specify HTTP Method to use
request.httpMethod = "GET"
// Send HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Check if Error took place
if let error = error {
print("Error took place (error)")
return
}
// Convert HTTP Response Data to a simple String
if let data = data {
// let json = try? JSONSerialization.jsonObject(with: data, options: [])
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
// try to read out a string array
print(type(of: json["results"]))
print(json["results"])
}
} catch let error as Error {
print("Failed to load: (error.localizedDescription)")
}
}
}
task.resume()
}
感谢您的任何帮助!
您确实应该使用Decodable,而不是尝试使用JSON解析它,因为这很容易导致错误,因为您通过字符串访问值并且不允许IDE为您提供帮助。
您需要创建一些对象来描述您在响应中得到的内容。
您的主 json 响应由以下内容组成
{
"count": 82,
"next": "http://swapi.dev/api/people/?page=2",
"previous": null,
"results": [...]
}
这允许您创建符合可解码的 People 结构。
struct People: Decodable {
let count: Int
let next: URL?
let previous: URL?
let results: [Person]
}
结果数组实际上是您所追求的,因为它包含有关一个人的所有信息。
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue",
"birth_year": "19BBY",
"gender": "male",
"homeworld": "http://swapi.dev/api/planets/1/",
"films": [
"http://swapi.dev/api/films/1/",
"http://swapi.dev/api/films/2/",
"http://swapi.dev/api/films/3/",
"http://swapi.dev/api/films/6/"
],
"species": [],
"vehicles": [
"http://swapi.dev/api/vehicles/14/",
"http://swapi.dev/api/vehicles/30/"
],
"starships": [
"http://swapi.dev/api/starships/12/",
"http://swapi.dev/api/starships/22/"
],
"created": "2014-12-09T13:50:51.644000Z",
"edited": "2014-12-20T21:17:56.891000Z",
"url": "http://swapi.dev/api/people/1/"
}
我们可以用以下名为 Person 的结构来表示这一点,该结构也符合 Decodable
struct Person: Decodable {
let name: String
let height: String
let mass: String
let hairColor: String
let skinColor: String
let birthYear: String
let gender: Gender
let homeworld: String
let films: [URL]
let species: [URL]
let vehicles: [URL]
let starships: [URL]
let created: Date
let edited: Date
let url: URL
}
enum Gender: String, Decodable {
case male
case female
case unknown = "n/a"
}
请注意结构中的名称与要返回的对象中的名称之间的一些差异。 例如hair_color
(蛇壳(和hairColor
(驼峰箱( 在 Swift 中,通常以后一种方式编写它,当我们使用可解码时,我们可以告诉我们的解码器使用自定义键解码策略。另请注意,我使用了性别enum
。这不是必需的,我们可以只使用String
.另请注意,created
和edited
是日期,但它们不符合 iso8601,但我们也可以指定自定义日期解码策略。
以下是我们如何解码您收到的数据。
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'"
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let people = try decoder.decode(People.self, from: data)
现在,我们可以将所有这些放在您的网络请求中,以获取以下内容:
func onLoad() {
let url = URL(string: "https://swapi.dev/api/people")
guard let requestUrl = url else { fatalError() }
// Create URL Request
var request = URLRequest(url: requestUrl)
// Specify HTTP Method to use
request.httpMethod = "GET"
// Send HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Check if Error took place
if let error = error {
print("Error took place (error)")
return
}
// Convert HTTP Response Data to a simple String
if let data = data {
do {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'"
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let people = try decoder.decode(People.self, from: data)
people.results.forEach { person in print(person) }
} catch {
print("Failed to load: (error)")
}
}
}
task.resume()
}
将results
转换为字典数组。方法如下
if let data = data {
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let results = json["results"] as? [[String: Any]] {
for result in results {
print(result)
}
}
} catch {
print("Failed to load: (error.localizedDescription)")
}
}
更好的方法:使用Codable
,JSONSerialization
感觉有点过时了。
相关链接:
- https://developer.apple.com/documentation/swift/codable
- https://www.swiftbysundell.com/basics/codable/