从 Swift 中的 API 响应访问 JSON 数组



我是编写 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.另请注意,creatededited是日期,但它们不符合 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)")
}
}

更好的方法:使用CodableJSONSerialization感觉有点过时了。

相关链接:

  1. https://developer.apple.com/documentation/swift/codable
  2. https://www.swiftbysundell.com/basics/codable/

最新更新