首页 > 解决方案 > JSONDecoder 和结构的问题(API 调用)

问题描述

因此,在解析来自 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 获得的。有任何想法吗?

标签: jsonswiftstruct

解决方案


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

删除这部分代码:

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所以正确的对象是字符串,而不是日期。您必须稍后在解析后将其转换为日期。


推荐阅读