【问题标题】:Can't return or decode data with JSONDecoder无法使用 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”的初始化;考虑用赋值替换。没有错误,替换为 _ 修复警告但没有任何反应,操场似乎无限期地运行

标签: json swift decode jsondecoder


【解决方案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】:

几个问题:

  1. URL 错误。没有www
  2. 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 我希望我能支持你的信息,但我不能,我很抱歉。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2019-02-06
  • 1970-01-01
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
  • 2020-01-07
  • 2020-04-24
相关资源
最近更新 更多