Swift - 使用 JSONDecoder 解码 JSON 数据

Posted

技术标签:

【中文标题】Swift - 使用 JSONDecoder 解码 JSON 数据【英文标题】:Swift - decode JSON data with JSONDecoder 【发布时间】:2020-07-14 20:43:15 【问题描述】:

我正在使用 CocktailDB API,但遇到了一些问题。 我创建了一个请求以从特定类别中获取鸡尾酒(每种鸡尾酒的名称和图像)。然后我尝试使用 Decodable 协议解析 JSON。但它不起作用并显示 JSON 错误。

因此,我想从以下请求“https://www.thecocktaildb.com/api/json/v1/1/list.php?c=list”中获取鸡尾酒类别,并且每个类别都有一个部分(显示标题中的类别名称)以及本节中的鸡尾酒。

我的饮料模型:

struct Drinks:Decodable 
    var drinks: [Drink]


struct Drink:Decodable 
    var strDrink: String
    var strDrinkThumb: String

我的类别模型:

struct Categories: Decodable 
    var drinks: [Drink]


struct Category: Decodable 
    var strCategory: String

我的代码:

    class ViewController: UIViewController 

        var drinks = [Drinks]()
        var categories = [Categories]()
        
        override func viewDidLoad() 
            super.viewDidLoad()
            downloadJSON()
        
        
        func downloadJSON() 
            let category = "Cocoa" // for example
            let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=\(category)")
            
            URLSession.shared.dataTask(with: url!)  (data, response, error) in
                
                if error == nil 
                    do 
                        self.drinks = try JSONDecoder().decode([Drinks].self, from: data!)
                        print(self.drinks)
                     catch 
                        print("JSON Error")
                    
                
            .resume()
        
    

extension ViewController: UITableViewDataSource, UITableViewDelegate 
    
    func numberOfSections(in tableView: UITableView) -> Int 
        return drinks.count
    
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
        return ""
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return 1
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        return UITableViewCell(style: .default, reuseIdentifier: "cell")
    
    
    

JSON 结构:

【问题讨论】:

而不是毫无意义的"JSON Error" 打印error 实例。它会准确地告诉你哪里出了问题。提示:根对象不是数组。 print(error) 并检查结果。 “JSON 错误”没有任何意义 " typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "期望解码 Array 但找到了一个字典。",底层错误: nil )) "显示错误 试试Categories.self而不是[Drinks].self @LeoDabus 不,根据 URL 上的 JSON,它是 Drinks.self 【参考方案1】:

您需要在此处进行一些额外的更改。将解码更改为Drinks.self。然后重新加载tableView。并返回节标题的字符串。代码如下:

解码:

var drinks = [Drink]() // modify the drinks property 

if let data == data 
    do 
        self.drinks = try JSONDecoder().decode(Drinks.self, from: data).drinks
        DispatchQueue.main.async 
            self.tableView.reloadData()
        
     catch 
        print(error)
    

还有titleForHeaderInSection方法:

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
    return drinks[section].strDrink

【讨论】:

与我的评论同步 ***.com/questions/62903702/… @VeronikaBabii 谢谢!试着让未来的问题更清楚。阅读帖子的人应该了解您所面临的确切问题。【参考方案2】:

错误很明显。而不是一个数组(正如你的 [Drinks].self 暗示的那样)你得到一个字典(映射到你的 Drinks 结构):

drinks = try JSONDecoder().decode(Drinks.self, from: data!)

这当然意味着:

var drinks 也变为 Drinks(而不是 [Drinks]

【讨论】:

但是饮料是[饮料],所以我不能将饮料分配给[饮料] ***.com/questions/62903702/… @VeronikaBabii 是的,当然它必须是Drinks 而不是[Drinks]。我会修改答案。 @VeronikaBabiii 不要强制解包数据。使用警卫guard let data = data else return @Alladinian 对不起,我应该将消息直接发送给 OP 我没有意识到我在标记你。检查错误是否为 nil 后强制解包是安全的,但不是编写代码的正确方法

以上是关于Swift - 使用 JSONDecoder 解码 JSON 数据的主要内容,如果未能解决你的问题,请参考以下文章

Swift 4 JSONDecoder 解码协议类型

使用 JSONDecoder 解码 [[String]]?

使用 JSONDecoder Swift 解码具有整数值的字符串键 [关闭]

Swift - 将类型动态传递给 JSONDecoder

Swift4 JSONDecoder 期望解码 Dictionary<String, Any> 但找到了一个数组 [重复]

使用 JSONDecoder 解码古怪的日期格式