在Swift 4中序列化JSON-确定数据类型的问题
我从一个在线API中获取一些JSON,并将结果放入数组中以备将来使用。到目前为止,所有的数据都很好,只是字符串数组,但我不知道如何处理其中一个结果 有人建议我用它来让它可读,它非常有用 这是获取JSON的函数:在Swift 4中序列化JSON-确定数据类型的问题,json,swift,codable,Json,Swift,Codable,我从一个在线API中获取一些JSON,并将结果放入数组中以备将来使用。到目前为止,所有的数据都很好,只是字符串数组,但我不知道如何处理其中一个结果 有人建议我用它来让它可读,它非常有用 这是获取JSON的函数: func getJSON(completionHandler: @escaping (Bool) -> ()) { let jsonUrlString = "https://api.nytimes.com/svc/topstories/v1/business.json?ap
func getJSON(completionHandler: @escaping (Bool) -> ()) {
let jsonUrlString = "https://api.nytimes.com/svc/topstories/v1/business.json?api-key=f4bf2ee721031a344b84b0449cfdb589:1:73741808"
guard let url = URL(string: jsonUrlString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data, err == nil else {
print(err!)
return
}
do {
let response = try
JSONDecoder().decode(TopStoriesResponse.self, from: data)
// Pass results into arrays (title, abstract, url, image)
for result in response.results {
let headlines = result.title
let abstracts = result.abstract
let url = result.url
self.headlines.append(headlines)
self.abstracts.append(abstracts)
self.urls.append(url)
}
let imageResponse = try
JSONDecoder().decode(Story.self, from: data)
for imageResults in imageResponse.multimedia {
let images = imageResults.url
self.images.append(images)
}
completionHandler(true)
} catch let jsonErr {
print("Error serializing JSON", jsonErr)
}
}.resume()
}
struct TopStoriesResponse: Decodable {
let status: String
let results: [Story]
}
struct Story: Decodable {
let title: String
let abstract: String
let url: String
let multimedia: [Multimedia]
}
struct Multimedia: Codable {
let url: String
let type: String
}
以下是序列化JSON的结构:
func getJSON(completionHandler: @escaping (Bool) -> ()) {
let jsonUrlString = "https://api.nytimes.com/svc/topstories/v1/business.json?api-key=f4bf2ee721031a344b84b0449cfdb589:1:73741808"
guard let url = URL(string: jsonUrlString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data, err == nil else {
print(err!)
return
}
do {
let response = try
JSONDecoder().decode(TopStoriesResponse.self, from: data)
// Pass results into arrays (title, abstract, url, image)
for result in response.results {
let headlines = result.title
let abstracts = result.abstract
let url = result.url
self.headlines.append(headlines)
self.abstracts.append(abstracts)
self.urls.append(url)
}
let imageResponse = try
JSONDecoder().decode(Story.self, from: data)
for imageResults in imageResponse.multimedia {
let images = imageResults.url
self.images.append(images)
}
completionHandler(true)
} catch let jsonErr {
print("Error serializing JSON", jsonErr)
}
}.resume()
}
struct TopStoriesResponse: Decodable {
let status: String
let results: [Story]
}
struct Story: Decodable {
let title: String
let abstract: String
let url: String
let multimedia: [Multimedia]
}
struct Multimedia: Codable {
let url: String
let type: String
}
我将结果组织到这些数组中:
var headlines = [String]()
var abstracts = [String]()
var urls = [String]()
var images = [String]()
我调用viewDidLoad中的函数
正如您在getJSON函数中看到的,我尝试使用
let imageResponse = try JSONDecoder().decode(Story.self, from: data)
for imageResults in imageResponse.multimedia {
let images = imageResults.url
self.images.append(images)
}
但是我得到了错误
CodingKeysstringValue:multimedia,intValue:nil],debugDescription:应解码数组,但找到了字符串/数据,UnderlineingError:nil
我很困惑,因为它说它需要一个数组,但却找到了一个字符串-图像不是数组吗,就像标题、摘要等一样?您将多媒体定义为数组。json中的某些部分没有任何多媒体,设置为空字符串:
multimedia: ""
你需要能够处理这两种情况。由于Codable设计用于处理具体类型,因此最好改用JSONSerialization
如果您非常喜欢使用Codable,那么可以操纵字符串形式的JSON响应,将multimedia:转换为您期望的格式,然后将其传递给解码器。例如,您可以将“多媒体”设置为可选,只需删除包含“多媒体:”的任何行。问题在于,多媒体要么是多媒体对象数组,要么是空字符串。您需要为Story编写一个自定义初始化器来处理这个问题
struct Story: Decodable {
let title: String
let abstract: String
let url: String
let multimedia: [Multimedia]
private enum CodingKeys: String, CodingKey {
case title
case abstract
case url
case multimedia
}
init(from decoder:Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
title = try container.decode(String.self, forKey: .title)
abstract = try container.decode(String.self, forKey: .abstract)
url = try container.decode(String.self, forKey: .url)
multimedia = (try? container.decode([Multimedia].self, forKey: .multimedia)) ?? []
}
}
问题在于来自服务器的JSON响应,其中一个JSON响应中的多媒体数组是空字符串。要处理这种情况,您需要手动实现initfrom decoder:method并处理空字符串大小写。此外,不需要创建单独的数组来存储值,您可以直接在完成处理程序闭包中传递TopStorieResponse结构,并在需要时在ViewController中获取值 假设您创建了一个包含successT和failureError的结果枚举,并将其传递到completionHandler中供ViewController处理
enum Result<T> {
case success(T)
case failure(Error)
}
struct Networking {
static func getJson(completionHandler: @escaping (Result<TopStoriesResponse>) -> ()) {
let jsonUrlString = "https://api.nytimes.com/svc/topstories/v1/business.json?api-key=f4bf2ee721031a344b84b0449cfdb589:1:73741808"
guard let url = URL(string: jsonUrlString) else {
return
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data, error == nil else {
completionHandler(Result.failure(error!))
return
}
do {
let topStoriesResponse: TopStoriesResponse = try JSONDecoder().decode(TopStoriesResponse.self, from: data)
print(topStoriesResponse.results.count)
completionHandler(Result.success(topStoriesResponse))
} catch {
completionHandler(Result.failure(error))
}
}.resume()
}
}
仔细查看JSON。注意多媒体:。这让我窒息。您必须编写一个自定义初始值设定项来处理这种数组或字符串的可能性。在基本数组中分解组织良好的结构是可怕的。不要那样做。假设您希望最终对数据进行排序…@vadian我正在使用数组,以便在cellForRowAt tableview方法中将标题等传递到标签中。出于好奇,什么是最好的选择,字典?@KingTim,不,将完整结构存储在数组中,然后在表视图方法中访问结构的相关属性直接从结构中设置值。谢谢,这对我来说是新的,所以我必须阅读自定义初始值设定项/编码键才能完全理解此解决方案。我快速尝试了一下您的代码,看看它是否能解决问题,结果控制台上显示了一个错误:序列化JSON KeyNotFoundCodingKeyStringValue:title,intValue:nil,Swift.DecodingError.ContextcodingPath:[],debugDescription:没有与key CodingKeyStringValue:\title\,intValue:nil\title\关联的值。,参考误差:零
struct Multimedia: Decodable {
let url: String
let image: String
let height: Float
let width: Float
}
struct Story: Decodable {
let title: String
let abstract: String
let url: String
let multimedia: [Multimedia]
private enum CodingKeys: String, CodingKey {
case title
case abstract
case url
case multimedia
}
init(from decoder:Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
title = try container.decode(String.self, forKey: .title)
abstract = try container.decode(String.self, forKey: .abstract)
url = try container.decode(String.self, forKey: .url)
multimedia = (try? container.decode([Multimedia].self, forKey: .multimedia)) ?? []
}
}
struct TopStoriesResponse: Decodable {
let status: String
let copyright: String
let num_results: Int
let results: [Story]
}