首页 > 解决方案 > 如何解码这个特定的 JSON 模型?

问题描述

编辑2:

在更改模型结构并在没有完成处理程序的情况下调用 JSON 解码器之后,我设法让它工作。谢谢大家的帮助。

固定代码:

模型


import Foundation

struct RatesResponse: Decodable, Hashable{
    let rates: [String: Double]
}

解码器类

import Foundation

class RatesModelData{
    public var rateCurrency = [String]()
    public var rateValue = [Double]()

    public func getRates(currency: String){
 
        guard let url = URL(string: "https://api.exchangerate.host/latest?base=\(currency)")
        else {
            print("URL is invalid")
            return
        }
        
        var request = URLRequest(url: url)

        let dataTask = URLSession.shared.dataTask(with: request){data, response, error in
            if let data = data {
                do{
                    let ratesResponse = try JSONDecoder().decode(RatesResponse.self, from: data)
                    
                    for rate in ratesResponse.rates{
                        self.rateCurrency.append(rate.key)
                        self.rateValue.append(rate.value)
                    }
                    
                    print(self.rateCurrency)

                } catch {
                        print(error)
                }
            }
        }

        dataTask.resume()
    }
}


编辑:

我已将 let rate = Rates 更改为 let rates = Rates,将解码器类修改为以下内容并添加了一个 do/catch 语句,但是我现在收到错误“给定的数据是无效的 JSON”。我也更新了代码片段。


我有这个 json 模型:

https://api.exchangerate.host/latest?base=usd

而且我一生都无法弄清楚如何正确解码它。我知道它是一本字典,因此必须对其进行解码,但我正在努力解决我做错的事情。

模型:

struct RatesResponse: Decodable, Hashable{
    let rates : Rates
}

struct Rates: Decodable, Hashable {
    let rates: [String: Double]
}

解码器类:

class RatesModelData{

    public func getRates(currency: String, _ completionHandler: @escaping(Rates) -> Void){
 
        guard let url = URL(string: "https://api.exchangerate.host/latest?base=\(currency)")
        else {
            print("URL is invalid")
            return
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue("application/json", forHTTPHeaderField: "ACCEPT")
        
        let dataTask = URLSession.shared.dataTask(with: request){data, response, error in
            do{
            if let data = data {
            
                if let ratesResponse = try JSONDecoder().decode(RatesResponse?.self, from: data){
                    completionHandler(ratesResponse.rates)
                    }
                    return
                }
                
                guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else{
                    print("Error with response: \(response)")
                    return
                }
                
                if let error = error {
                    print("Error Thrown : \(error)")
                    return
                }
           } catch {
                print(error)
           }
        }

        dataTask.resume()
    }
}

理想情况下,我希望将其显示在列表视图(SwiftUI)中,但现在只是就我的解码出错的地方征求一些建议。

标签: iosjsonswift

解决方案


您的对象模型表明与键关联的值rates是具有另一个rates键的另一个对象。但这与您提供的 JSON 不匹配。与顶级rates键关联的值只是一个字典。

因此,您可以执行以下操作并完成它:

struct ResponseObject: Decodable {
    let rates: [String: Double]
}

或者,如果您想捕获此 JSON 中的所有内容,您可以添加其他属性:

struct Motd: Decodable {
    let msg: String
    let url: URL
}

struct ResponseObject: Decodable {
    let motd: Motd
    let success: Bool
    let base: String
    let date: Date
    let rates: [String: Double]
}

接着:

do {
    let formatter = DateFormatter()
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.dateFormat = "yyyy-MM-dd"
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .formatted(formatter)
    let responseObject = try decoder.decode(ResponseObject.self, from: data)
    print(responseObject)
} catch {
    print(error)
}

FWIW、basedaterates对象可能/应该是可选的,例如:

struct ResponseObject: Decodable {
    let motd: Motd
    let success: Bool
    let base: String?
    let date: Date?
    let rates: [String: Double]?
}

为了确认,我们需要看看格式良好的非成功响应是什么样的。浏览文档并不是很明显,但类似上面的内容可能是您想要的,在哪里JSONDecoder能够成功解码成功响应和失败响应。


推荐阅读