【问题标题】:Issue with JSONDecoder and Structs (API Call)JSONDecoder 和结构的问题(API 调用)
【发布时间】:2021-03-01 16:37:02
【问题描述】:

所以我在解析来自 API 的数据时遇到了 JSON 解码器的另一个问题。我收到错误“解析 JSON 错误”。我叫这个api点:https://newsapi.org/v2/top-headlines?category=Science&apiKey=afd219a70f3d47f784e588548c0f7810

这是我拨打电话的代码:

        
        var baseURL = "https://newsapi.org/v2/top-headlines?category=\(category)&apiKey=afd219a70f3d47f784e588548c0f7810"

        let defaultSession = URLSession(configuration: .default)
        if let url = URL(string: baseURL) {
          let request = URLRequest(url: url)
          let dataTask = defaultSession.dataTask(with: request) { (data, response, error) in

            guard error == nil else {
              print("error: ", error!)
              return
            }

            guard let data = data else {
              print("No data object")
              return
            }

            guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
              print("response is: ", response!)
              return
            }

            guard let mime = response?.mimeType, mime == "application/json" else {
              print("Wrong MIME type!")
              return
            }
            
            guard let articles = try? JSONDecoder().decode(ArticleList.self, from: data) else {
              print("Error Parsing JSON")
              return
            }
            
            DispatchQueue.main.async {
                self.articles = articles.articles
                print(self.articles)
            }
            
          }
          dataTask.resume()
        }
    }

这是我的结构:

// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
//   let welcome = try? newJSONDecoder().decode(Welcome.self, from: jsonData)

import Foundation

// MARK: - Welcome
struct ArticleList: Codable {
    let status: String
    let totalResults: Int
    let articles: [Article]
}

// MARK: - Article
struct Article: Codable {
    let source: Source
    let author: String?
    let title: String
    let articleDescription: String?
    let url: String
    let urlToImage: String?
    let publishedAt: Date
    let content: String

    enum CodingKeys: String, CodingKey {
        case source, author, title
        case articleDescription = "description"
        case url, urlToImage, publishedAt, content
    }
}

// MARK: - Source
struct Source: Codable {
    let id: JSONNull?
    let name: String
}

// MARK: - Encode/decode helpers

class JSONNull: Codable, Hashable {

    public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
        return true
    }

    public var hashValue: Int {
        return 0
    }

    public init() {}

    public required init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if !container.decodeNil() {
            throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encodeNil()
    }
}

我假设我的结构有问题,这很奇怪,因为我是从 quicktype.io 获得的。有什么想法吗?

【问题讨论】:

  • 在您之前关于 json 解码的两个问题中,我已经告诉您如何处理解码错误,但您根本不在乎,是吗?

标签: json swift struct


【解决方案1】:

我也认为它可能与结构有关。要验证您的解析问题,请尝试以下操作:

去掉这部分代码:

guard let articles = try? JSONDecoder().decode(ArticleList.self, from: data) else {
              print("Error Parsing JSON")
              return
}

DispatchQueue.main.async {
       self.articles = articles.articles
       print(self.articles)
}

并使用它:

do {
    let articles = try JSONDecoder().decode(ArticleList.self, from: data)
    DispatchQueue.main.async {
         self.articles = articles.articles
         print(self.articles)
    }
} catch {
    print(error)
}

这将在日志窗口中显示解析错误。 json很大,所以它可能不止一个。解析错误应该是因为某些模型返回某些变量为空。

解决此问题的一种方法是接受一些变量作为可选变量。像这样:

struct ArticleList: Codable {
    let status: String?
    let totalResults: Int?
    let articles: [Article]?
}

但最好的方法是让您知道哪些是可选的。测试 api 并阅读解析错误的日志将帮助您解决这个问题。

我马上看到的一个问题是你的变量Article.publishedAt 它返回一个像这样的字符串:2021-03-01T14:14:00Z 所以正确的对象是字符串,而不是日期。您必须稍后在解析后将其转换为日期。

【讨论】:

  • 谢谢!现在信息量更大了。这是它吐出的错误: Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "articles", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: " publishedAt", intValue: nil)], debugDescription: "预期解码 Double 但找到了一个字符串/数据。",底层错误: nil))
【解决方案2】:

Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "articles", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "publishedAt", intValue: nil )], debugDescription: "预期解码 Double 但找到了一个字符串/数据。",底层错误: nil))

错误明确指出数组articles 中键publishedAt 的值不是Double(默认日期解码策略),而是ISO8601 String

您必须指定正确的日期解码策略(我盗版 Felipe 回答的基本代码),是的,始终使用do - catch

do {
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .iso8601
    let articles = try decoder.decode(ArticleList.self, from: data)
    DispatchQueue.main.async {
         self.articles = articles.articles
         print(self.articles)
    }
} catch {
    print(error)
}

旁注:

  • 您不需要URLRequest,有一个具有相同语法的数据任务方法采用URL。
  • 删除JSONNull 的东西,并将Source 中的id 声明为String?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-06
    • 2013-05-17
    • 1970-01-01
    • 2017-09-20
    • 2020-11-18
    • 2019-08-29
    相关资源
    最近更新 更多