无法解码 JSON 因为 Content-Type 丢失

Can't decode JSON because Content-Type missed

我的 JSONDecoder().decode 无法将数据解码为 json 格式,因为服务器响应有 Content-Type 这样的 "* \ *;charset=utf8".

遇到这种情况我该怎么办?有任何想法吗? API link

我的代码:

private static let livePhotoUrlString = "https://m1.kappboom.com/livewallpapers/info?o=0&v=575"

static func getLivePhotos(completionHandler: @escaping (([LivePhoto]) -> Void)) {
    guard let livePhotoUrl = URL(string: livePhotoUrlString) else { return }
    let semaphore = DispatchSemaphore(value: 0)

    URLSession.shared.dataTask(with: livePhotoUrl) { (data, response, error) in
         do {
             guard let data = data else { return }
             let livePhotos = try JSONDecoder().decode([LivePhoto].self, from: data)
             completionHandler(livePhotos)
         } catch {
             completionHandler([])
         }
         semaphore.signal()
     }.resume()
     semaphore.wait()
}

我的实体(LivePhoto):

class LivePhoto: Decodable {

    init(smallUrl: String, largeUrl: String, movieUrl: String, id: Int, isLocked: Bool, promotionalUnlock: Bool) {
        self.smallUrl = smallUrl
        self.largeUrl = largeUrl
        self.movieUrl = movieUrl
        self.id = id
        self.isLocked = isLocked
        self.promotionalUnlock = promotionalUnlock
    }

    var smallUrl: String
    var largeUrl: String
    var movieUrl: String
    var id: Int
    var isLocked: Bool
    var promotionalUnlock: Bool
}

响应headers:

正确答案(另一个API):

您需要使用 json 中的键名,或者使用转换后的名称编写枚举,但最好使用 convertFromSnakeCase

  func getLivePhotos(completionHandler: @escaping (([LivePhoto]) -> Void)) {
        guard let livePhotoUrl = URL(string: livePhotoUrlString) else { return }

        URLSession.shared.dataTask(with: livePhotoUrl) { (data, response, error) in
              print(data)
            do {
                guard let data = data else { return }
                let dec = JSONDecoder()
                dec.keyDecodingStrategy = .convertFromSnakeCase
                let livePhotos = try dec.decode([LivePhoto].self, from: data)
                completionHandler(livePhotos)
            } catch {
                print(error)
                completionHandler([])
            }

            }.resume()

    }



}

struct LivePhoto: Codable {
    let id: Int
    let smallUrl, largeUrl: String
    let movieUrl: String
    let isLocked, promotionalUnlock: Bool

}

此外,始终在 catch 块内 print(error) 是最佳做法,这样您就可以知道错误并修复,这里没有信号量的位置,它只是完成的工作,您也可以显示一个 activity 指示器,直到请求完成作为更好的 UX

你确定这是问题所在吗,在我看来你需要为你的结构定义编码键

enum CodingKeys: String, CodingKey {
    case smallUrl = "small_url"
    case largeUrl = "large_url"
    case movieUrl = "movie_url"
    case isLocked = "is_locked"
    case promotionalUnlock = "promotional_unlock"
    case id
}

错误与内容类型无关。

而不是忽略catch块中的错误打印它,解码错误非常具有描述性。

} catch {
    print(error)
    completionHandler([])
}

上面写着

keyNotFound(CodingKeys(stringValue: "smallUrl", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"smallUrl\", intValue: nil) (\"smallUrl\").", underlyingError: nil))

您可以立即看到键是 small_url 并且您的结构成员是 smallUrl.

最简单的解决方案是添加convertFromSnakeCase密钥解码策略

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let livePhotos = try decoder.decode([LivePhoto].self, from: data)

而且您不需要 class 中的 init 方法。将其声明为具有常量成员的结构

struct LivePhoto: Decodable {
    let smallUrl, largeUrl, movieUrl: String
    let id: Int
    let isLocked: Bool
    let promotionalUnlock: Bool
}

请删除这个可怕的 semaphore。无论如何,当您使用完成处理程序时,这是毫无意义的。