【问题标题】:Decode nested array from dictionary Swift [duplicate]从字典Swift解码嵌套数组[重复]
【发布时间】:2022-01-24 08:14:05
【问题描述】:

我在尝试解码 Dictionary JSON 时收到此错误:"Expected to decode Array<Any> but found a dictionary instead."

JSON 看起来像这样:

{
  "status": "success",
  "totalResults": 69,
  "results": []
}

如何跳过 status 和 totalResults 并仅解码结果?

我的代码:

struct News: Decodable {
  var title: String
  var content: String
}

let jsonUrl = URL(string: "https://newsdata.io/api/1/news?apikey=pub_297524faa4f21de311b826df181186f8e33b&q=travelling&language=en")

var news = [News]()

URLSession.shared.dataTask(with: jsonUrl!) { data, response, error in
    do {
        news = try JSONDecoder().decode([News].self, from: data!)
        news.forEach { print($0.title) }
    } catch {
        print(error)
    }
}.resume()

PS 在这里提问,因为 99% 的 youtube 教程只解释如何解码数组

【问题讨论】:

  • .decode([News].self, from: data!):你为什么写[New].self而不是New.self?我知道两者的含义,但我问你为什么(让你明白)。
  • 这是我的第一次尝试,所有这些都是我看 youtube 教程完成的 :) 我仍然不完全了解发生了什么
  • 试试 JSONDecoder().decode(News.self, from: data!) 试试这个。你得到的响应是 JSON 对象而不是数组。所以它不应该是[新闻]
  • “我怎样才能跳过 status 和 totalResults 并只解码结果?”,如果你愿意,你可以省略这些字段,但你仍然需要从顶部解码,所以你需要一个包含result 数组的结构并在解码时使用它。

标签: json swift utf-8


【解决方案1】:

要解码您的 json 数据,您必须首先获得与数据匹配的正确 swift 数据模型。 一种快速的方法是将您的 json 数据复制并粘贴到“https://quicktype.io/”中。 该站点将为您提供所需的快速结构。然后你需要进行请求调用 到服务器。鉴于 swift 数据结构,这是执行此操作的代码。

import SwiftUI

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State var results = [Result]()
    
    var body: some View {
        List {
            ForEach(results) { news in
                VStack {
                    Text(news.title)
                    Text(news.link).foregroundColor(.blue)
                }
            }
        }
        .onAppear {
            loadNews()
        }
    }
    
    func loadNews() {
        let jsonUrl = URL(string: "https://newsdata.io/api/1/news?apikey=YOURAPIKEY&q=travelling&language=en")
        guard let url = jsonUrl else { return }
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data else { return }
            do {
                let response = try JSONDecoder().decode(Response.self, from: data)
                results = response.results
            }
            catch {
                print("---> error: \(error)")
            }
        }
        task.resume()
    }
}

struct Response: Codable {
    let status: String
    let totalResults: Int
    let results: [Result]
    let nextPage: Int
}

struct Result: Identifiable, Codable {
    let id = UUID()
    let title: String
    let link: String
    let keywords: [String]?
    let creator: [String]?
    let videoURL: JSONNull?
    let resultDescription, content: String?
    let pubDate: String
    let fullDescription: String?
    let imageURL: String?
    let sourceID: String

    enum CodingKeys: String, CodingKey {
        case title, link, keywords, creator
        case videoURL = "video_url"
        case resultDescription = "description"
        case content, pubDate
        case fullDescription = "full_description"
        case imageURL = "image_url"
        case sourceID = "source_id"
    }
}

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()
    }
}

PS:请勿发布您的 API 密钥。立即将其从您的问题中删除。

EDIT-1:

如果出于某种原因确实需要,不要解码statustotalResults 使属性let 并给它们一个初始值,如下所示。 Xcode 会告诉你这些不会被解码。

struct Response: Codable {
    let results: [Result]
    let status: String = ""   // <-- here give the let a value
    let totalResults: Int = 0 // <-- here give the let a value
    let nextPage: Int = 0     // <-- here give the let a value
}

如果您不想使用这些字段,请删除它们:

struct Response: Codable {
    let results: [Result]
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-03
    • 1970-01-01
    • 2015-10-03
    • 2018-04-14
    • 2019-05-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多