【问题标题】:Retrieving data from nested JSON从嵌套的 JSON 中检索数据
【发布时间】:2022-01-18 07:32:52
【问题描述】:

我正在努力理解如何从我的 JSON 文件中获取数据并使用它(例如在按下按钮时,它会更改标签文本或描述),尽管我已经阅读了无数的线程,但我仍然卡住了,但这里是我从这些文章和 stackoverflow 线程中尝试做的事情。

假设我有一个名为 Animals.json 的 json 文件:

{
"animal": [

    {
       "name":"Cat",
       "image" : "cat.jpg",
       "category": "mammal"
    },
   
    {
        "name":"Eagle",
        "image" : "eagle.jpg",
        "category": "Bird",
    }
 ]
}

我在一个名为 Animals.swift 的单独文件中创建了一个结构:

struct Animals: Codable {
var animal: [Animal]
}

然后是另一个名为 Animal.swift 的文件:

struct Animal : Codable {
var name: String
var image: String
var category: String   
}

这是我的 MainVC 中的代码:

import UIKit

class InstrumentsVC: UIViewController {
override func viewDidLoad() {
        super.viewDidLoad()
    
    if let localData = self.readLocalFile(forName: "Animals") {
        self.parse(jsonData: localData)
    }
        
    
}


private func readLocalFile(forName name: String) -> Data? {
    do {
        if let bundlePath = Bundle.main.path(forResource: name,
                                             ofType: "json"),
            let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8) {
            return jsonData
        }
        
    } catch {
        print(error)
    }
    return nil
}

private func parse(jsonData: Data) {
    do {

        let decodedData = try JSONDecoder().decode(Animals.self,
                                                   from: jsonData)
        print("animal: ", decodedData.animal.first?.name ?? "")
        
    } catch {
        print("decode error")
    }
}

private func loadJson(fromURLString urlString: String,
                      completion: @escaping (Result<Data, Error>) -> Void) {
    if let url = URL(string: urlString) {
        let urlSession = URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in
            if let error = error {
                completion(.failure(error))
            }
            
            if let data = data {
                completion(.success(data))
            }
        }
        urlSession.resume()
    }
}

@IBAction func nextCategory(_ sender: UIButton) {
    //I'll write a function that allows changing categories then loading an image that will update an outlet and labels
}

}

我还没有开始调用 loadJson 函数的部分,因为我打印了一个解码错误,而且我是 JSON 解码/解析的新手,我想知道哪里出了问题以及如何修复那。 这个想法是当我要按下一个按钮时,它会循环遍历我的 JSON 的“类别”部分(我还没有编写函数,但这应该不是问题),然后我有另一个按钮可以通过“名称”触发循环(这是一个简化的 JSON,因为会有很多来自同一类别的元素;它也不会是动物,而是一些与音乐相关的东西,我只是认为这样更容易理解用动物而不是放一些音乐理论的东西)。

现在我被打印在 readLocalFile 函数中的错误卡住了;然后从我读过的多篇文章中,我显然需要调用 loadJson 函数,但我还不太确定如何完成所有这些以获取每个元素的名称/图像/类别。 谢谢

【问题讨论】:

  • 您的代码对我有用。您确定您在此处粘贴的代码与您的项目中的代码相同吗?
  • 是的,除了其他插座和按钮外,它的代码完全相同
  • 你的json不正确"category": "Bird",结尾应该有','
  • 感谢 Noor Ahmed,这是一个错字,我忘记在这里更正了,因为我在这篇文章中更改了我的 json 的实际内容,以便更容易理解,但我的 json 文件格式正确;但是,由于下面的答案,我找到了错误的原因:我的结构的名称与我的 JSON 略有不同。谢谢大家!
  • 无关,但let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8)应该是let jsonData = try Data(contentsOfFile: bundlePath),不需要转换...

标签: json swift


【解决方案1】:

我为你写了完整的答案..以此作为参考..祝你好运。

import UIKit

struct Animal : Codable {
    var name: String
    var image: String
    var category: String
}

struct Animals: Codable {
    var animal: [Animal]
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let data = Data.readJsonFile(withName: "Animals")
        
        do {
            let animalList = try JSONDecoder().decode(Animals.self, from: data!)
            for item in animalList.animal {
                print(item.name)
            }
        } catch {
            print("error")
        }
    }
}



extension Data {
    static func readJsonFile(withName name: String) -> Data? {
        do {
            if let bundlePath = Bundle.main.path(forResource: name, ofType: "json"),
                let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8) {
                return jsonData
            }
        } catch {
            print(error)
        }
        return nil
    }
}

【讨论】:

  • 谢谢!但它仍然在控制台中打印错误并且不打印任何 item.name
  • 上面的代码对我来说很好用。确保你添加了 Animals.json 文件。
  • 是的,问题出在我的结构中,一些参数的名称与 json 文件的名称不完全相同(有几个错别字),但现在它可以工作了,非常感谢!
  • print("error"):不,请不要那样做。而是:print("error: \(error)")。打印错误,它对失败的原因进行了有意义的描述,并让您知道如何修复它!
【解决方案2】:

您共享的代码实际上正在运行。

如果您遇到解码错误。可能原因会是

  1. 缺少任何空值或任何结构(动物)成员(名称、图像、类别)

所以你可以像可选的那样改变你的结构成员。因此,如果有任何失败情况,它可以保持零值。制作这样的结构

struct Animals: Codable {
var animal: [Animal]?
}

struct Animal : Codable {
var name: String?
var image: String?
var category: String?
}

访问值时不要忘记安全解包。

private func parse(jsonData: Data) {
    do {
        let decodedData = try JSONDecoder().decode(Animals.self, from: jsonData)
        print("animal: ", decodedData.animal?.first?.name ?? "")
    } catch {
        print("decode error")
    }
}

还有一个原因是类型转换错误。例如,成员(名称、图像、类别)在结构中被声明为字符串,但在 JSON 中,如果任何值是整数或不是字符串,则将是解码错误。你应该更正json

【讨论】:

  • 就是这样!我的结构成员和我的 json 成员之间的不匹配,该死的错别字......
  • print("decode error"):不,请不要那样做。而是:print("decode error: \(error)")。打印错误,它对失败的原因进行了有意义的描述,并让您知道如何修复它!
猜你喜欢
  • 2017-06-09
  • 2015-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-18
  • 2020-05-10
  • 1970-01-01
  • 2018-12-15
相关资源
最近更新 更多