编写 JSON 序列化函数



我想创建一个函数,它接受一些参数,然后从Web API输出我需要的数据。显然,很多时候我需要自定义它以适应用例,但只是为了好玩,我试图找出一个成功解析 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)
self.storyData = response.results
completionHandler(true)
} catch let jsonErr {
print("Error serializing JSON", jsonErr)
}
}.resume()
}

唯一会因情况而异的三件事(同样,在最基本的情况下(是指向 API 的 url 链接、我为查找所需数据片段而设置的结构,以及数据请求完成后将结果输出到的数组。

我可以修剪脂肪并做类似的事情吗

func jsonFetcher(apiLink: String, structToDecode: String, arrayThatHoldsResponse: [String], completionHandler: @escaping (Bool) -> ()) {
let jsonUrlString = apiLink
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(structToDecode, from: data)
arrayThatHoldsResponse = response.results
completionHandler(true)
} catch let jsonErr {
print("Error serializing JSON", jsonErr)
}
}.resume()
}

我只是不确定structToDecodearrayThatHoldsResponse的数据类型(在上面的示例函数中,我只是使用String作为占位符(,假设它们看起来像

结构体

struct TopStoriesResponse: Decodable {
let status: String
let results: [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)) ?? []
}
}

数组

var storyData = [Story]()

这样我就可以打电话

jsonFetcher(apiLink: link, structToDecode: myStruct, arrayThatHoldsResponse: myArray, completionHandler: <#T##(Bool) -> ()#>)

感谢您的任何帮助!

泛型的力量。您可以创建一个泛型函数,其中参数是urlStringT继承Decodable协议。

这样,只要您的 Model 继承了Decodable协议,您就可以每次调用此函数。

func fetchData<T: Decodable>(urlString: String, completion: @escaping (T) -> ()) {
let url = URL(string: urlString)!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print(error.localizedDescription)
}
guard let data = data else { return }
do {
let object = try JSONDecoder().decode(T.self, from: data)
completion(object)
} catch let jsonErr {
print("Failed to decode json:", jsonErr)
}
}.resume()
}

如何调用函数:

struct User: Decodable { }
fetchData(urlString: "yourUrl") { (User: User) in
// Handle result
}

struct Animal: Decodable { }
fetchData(urlString: "yourUrl") { (animal: Animal) in
// Handle result
}

// Or if you want to fetch an array of users instead
fetchData(urlString: "yourUrl") { (users: [User]) in
// Handle result
}

在您的情况下

var storiesData: [Story] = []
fetchData(urlString: "https://api.nytimes.com/svc/topstories/v1/business.json?api-key=f4bf2ee721031a344b84b0449cfdb589:1:73741808") { (stories: [Story] in
storiesData = stories
DispatchQueue.main.async {
self.tableView.reloadData()
}
}

根据雅各布的回答,我建议也返回一个可能的错误。

要保留泛型布局,请将一个也是泛型枚举声明为返回类型

enum FetchResult<T> {
case success(T), failure(Error)
}

并使用传递的静态类型返回FetchResult

func fetchData<T: Decodable>(url: URL, completion: @escaping (FetchResult<T>) -> Void) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {completion(.failure(error!)); return } 
do {
let object = try JSONDecoder().decode(T.self, from: data)
completion(.success(object))
} catch {
completion(.failure(error))
}
}.resume()
}

并使用它

let jsonUrl = URL(string: "https://api.nytimes.com/svc/topstories/v1/business.json?api-key=••••••••••••••••••:1:73741808")!
fetchData(url: jsonUrl) { (result : FetchResult<TopStoriesResponse>) in
switch result {
case .success(let object): print(object) // do something with object
case .failure(let error): print(error) // handle the error
}
}

最新更新