无法使用 JSONDecoder 返回或解码数据
Posted
技术标签:
【中文标题】无法使用 JSONDecoder 返回或解码数据【英文标题】:Can't return or decode data with JSONDecoder 【发布时间】:2017-12-16 16:26:16 【问题描述】:我正在学习在 Xcode 游乐场中使用 JSON 解码数据,但无法弄清楚我的代码有什么问题,我无法返回数据也无法对其进行解码。 这是我的代码:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
extension URL
func withQueries(_ queries: [String: String]) -> URL?
var components = URLComponents(url: self, resolvingAgainstBaseURL: true)
components?.queryItems = queries.flatMap URLQueryItem(name: $0.0, value: $0.1)
return components?.url
struct StoreItems: Codable
let results: [StoreItem]
struct StoreItem: Codable
var name: String
var artist: String
var kind: String
var artworkURL: URL
var description: String
enum CodingKeys: String, CodingKey
case name = "trackName"
case artist = "artistName"
case kind
case artworkURL
case description
enum AdditionalKeys: String, CodingKey
case longDescription
init(from decoder: Decoder) throws
let valueContainer = try decoder.container(keyedBy: CodingKeys.self)
name = try valueContainer.decode(String.self, forKey: CodingKeys.name)
artist = try valueContainer.decode(String.self, forKey: CodingKeys.artist)
kind = try valueContainer.decode(String.self, forKey: CodingKeys.kind)
artworkURL = try valueContainer.decode(URL.self, forKey: CodingKeys.artworkURL)
if let description = try? valueContainer.decode(String.self, forKey: CodingKeys.description)
self.description = description
else
let additionalValues = try decoder.container(keyedBy: AdditionalKeys.self)
description = (try? additionalValues.decode(String.self, forKey: AdditionalKeys.longDescription)) ?? ""
func fetchItems(matching query: [String: String], completion: @escaping ([StoreItem]?) -> Void)
let baseURL = URL(string: "https://www.itunes.apple.com/search?")!
guard let url = baseURL.withQueries(query) else
completion(nil)
print("Unable to build URL with supplied queries.")
return
let task = URLSession.shared.dataTask(with: url) (data, response, error) in
let decoder = JSONDecoder()
if let data = data,
let storeItems = try? decoder.decode(StoreItems.self, from: data)
completion(storeItems.results)
else
print("Either no data was returned or data was not properly decoded.")
completion(nil)
return
task.resume()
let query: [String: String] = [
"term": "Inside Out 2015",
"media": "movie",
"lang": "en_us",
"limit": "10"
]
fetchItems(matching: query) (items) in
print(items)
这是打印到控制台的内容,我猜这表明我的“任务”有问题:
要么没有返回数据,要么没有正确解码数据。
无
【问题讨论】:
请,请不要忽略错误。JSONDecoder
抛出几个不同的错误,很好地描述了问题。
感谢您的回答 Vadian 但没有错误,只是没有结果...
肯定存在 is 错误,但您使用 try?
语法忽略了它。实现do - catch
块并读取错误。
没有错误,因为您使编译器使用try?
将错误转换为nil
值。将 let storeItems = try? decoder.decode(StoreItems.self, from: data)
更改为 do let storeItems = try decoder.decode(StoreItems.self, from: data) catch print(error)
并在问题中包含错误。在寻求 JSON 解析帮助时,您还应该在问题中包含实际的 JSON 响应。
当我执行 do - catch 时,我收到以下警告:从未使用过不可变值“storeItems”的初始化;考虑用赋值替换。没有错误,替换为 _ 修复警告但没有任何反应,操场似乎无限期地运行
【参考方案1】:
这不是具体的解决方案,而是如何调试解码的建议。
用下面的代码替换以if let data = data,
开头的整个表达式并调试代码。从dataTask
返回的潜在(在您的情况下是确定的)错误也得到处理。
guard let data = data else
print(error!)
completion(nil)
return
do
let storeItems = try decoder.decode(StoreItems.self, from: data)
completion(storeItems.results)
catch DecodingError.dataCorrupted(let context)
print(context.debugDescription)
catch DecodingError.keyNotFound(let key, let context)
print("Key '\(key)' not Found")
print("Debug Description:", context.debugDescription)
catch DecodingError.valueNotFound(let value, let context)
print("Value '\(value)' not Found")
print("Debug Description:", context.debugDescription)
catch DecodingError.typeMismatch(let type, let context)
print("Type '\(type)' mismatch")
print("Debug Description:", context.debugDescription)
catch
print("error: ", error)
completion(nil)
【讨论】:
非常感谢 Vadian,您的调试代码对我来说很有价值。我从守卫完成中得到零,然后没有别的。问题出在数据上吗? 我更新了答案以在guard
语句的else
子句中打印错误。正如 Rob 在他的回答中提到的那样,URL 是错误的。他正在提供一个完整的解决方案。【参考方案2】:
几个问题:
-
URL 错误。没有
www
。
artworkURL
似乎是可选的,因为您的搜索没有返回该键的值。
当我修复这些时,它可以工作:
extension URL
func withQueries(_ queries: [String: String]) -> URL?
var components = URLComponents(url: self, resolvingAgainstBaseURL: true)
components?.queryItems = queries.flatMap URLQueryItem(name: $0.0, value: $0.1)
return components?.url
struct StoreItems: Codable
let results: [StoreItem]
struct StoreItem: Codable
var name: String
var artist: String
var kind: String
var artworkURL: URL?
var shortDescription: String?
var longDescription: String?
enum CodingKeys: String, CodingKey
case name = "trackName"
case artist = "artistName"
case kind, artworkURL, shortDescription, longDescription
enum FetchError: Error
case urlError
case unknownNetworkError
func fetchItems(matching query: [String: String], completion: @escaping ([StoreItem]?, Error?) -> Void)
let baseURL = URL(string: "https://itunes.apple.com/search")!
guard let url = baseURL.withQueries(query) else
completion(nil, FetchError.urlError)
return
let task = URLSession.shared.dataTask(with: url) data, _, error in
guard let data = data, error == nil else
completion(nil, error ?? FetchError.unknownNetworkError)
return
do
let storeItems = try JSONDecoder().decode(StoreItems.self, from: data)
completion(storeItems.results, nil)
catch let parseError
completion(nil, parseError)
task.resume()
还有:
let query = [
"term": "Inside Out 2015",
"media": "movie",
"lang": "en_us",
"limit": "10"
]
fetchItems(matching: query) items, error in
guard let items = items, error == nil else
print(error ?? "Unknown error")
return
print(items)
注意,我建议您将错误添加到完成处理程序中,以便您可以看到 为什么它失败(在您的情况下,第一个问题是 URL 错误)。
【讨论】:
Rob 我希望我能支持你的信息,但我不能,我很抱歉。非常感谢!以上是关于无法使用 JSONDecoder 返回或解码数据的主要内容,如果未能解决你的问题,请参考以下文章
Swift - 使用 JSONDecoder 解码 JSON 数据
如何在 swift 4.1 和 xcode 9.3 中使用 JSONDecoder 解码嵌套的 JSON 数组和对象?