【问题标题】:How to decode this particular JSON model?如何解码这个特定的 JSON 模型?
【发布时间】:2021-10-10 00:15:41
【问题描述】:

编辑 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”。我也更新了代码 sn-ps。


我有这个 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) 中,但现在只是就我的解码出错的地方寻求一些建议。

【问题讨论】:

  • 让利率:利率 -> 让利率:利率
  • 首先,告诉我您在哪里找到了该示例代码,因为使用try ? 进行 JSON 解码的人需要好好谈谈。使用do/try/catch 并打印结果错误,你会看到出了什么问题,即json 有rates 而你有rate
  • 我在实施您的建议后编辑了我的帖子,但我收到一条错误消息,说我的 json 无效,我是否错误地声明了我的模型?
  • 你想要decode(RatesResponse.self, from: data) - 你不能解码一个可选的。
  • 照顾更简单的键(成功、基数、日期),首先,看看你是否能读懂它们。

标签: ios json swift


【解决方案1】:

您的对象模型表明与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 将能够成功解码成功响应和失败响应。

【讨论】:

    【解决方案2】:

    去年我经历了将 JSON 解析为结构的困难,所以我很乐意帮助你。这绝对是一个具有挑战性的概念:

    首先,让我解释一下你的结构有什么问题:

    struct RatesResponse: Decodable, Hashable{
        let rates : Rates
    // "rates" is a root-level key in your JSON. 
    // You're giving it the value of your structure Rates,
    // which also has a key titled "rates".
    }
    
    struct Rates: Decodable, Hashable {
        let rates: [String: Double]
    }
    

    如果可以使用这些结构来解析它,您的 JSON 将如下所示:

    {
        "rates": {
            "rates": {
                "AED": 3.671827,
                "AFN": 80.098152
            }
        }
    

    在您的实际 JSON 中,您的“rates”键具有对象(或字典,它只不过是具有键和值的数组)的值每个货币的变量,所以只需在结构中使用一个数组,元素类型为String: Double

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

    JSON 键“rates”具有字典的值,其中键是字符串,值是双精度值。

    在你的 URLSession 中解码后,你可以这样做:

    for rate in ratesResponse.rates {
        let currency = rate.key // AED, AFN, etc.
        let exchangeRate = rate.value // 3.671827, 80.098152, etc.
        // With these variables, you can append each of them to a new, separate array for use in your SwiftUI list.
    }
    

    【讨论】:

    • 非常感谢您向我解释!经过几次修改+您的建议后,我的代码现在可以正常工作了,我对它的理解更好!
    • 很高兴您能理解。如果您对解析 JSON 数据有任何疑问,请随时 PM 我。这绝对是一个令人困惑的话题:)
    猜你喜欢
    • 1970-01-01
    • 2022-01-21
    • 2011-01-15
    • 1970-01-01
    • 2019-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多