【问题标题】:Parsing JSON data to fill a TableView解析 JSON 数据以填充 TableView
【发布时间】:2019-05-07 19:22:11
【问题描述】:

我有一个 JSON(目前在本地),我想对其进行解析以将这些数据放入 listView 中。

我已经创建了视图并尝试了一些方法(如本教程:https://www.journaldev.com/21839/ios-swift-json-parsing-tutorial)来解析 JSON,但没有成功。

这是我尝试过的一些代码:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet weak var labelHeader: UILabel!
@IBOutlet weak var tableView: UITableView!

var channelList = [channelData]()

override func viewDidLoad() {
    super.viewDidLoad()
let url = Bundle.main.url(forResource: "channels", withExtension: "json")

    guard let jsonData = url
        else{
            print("data not found")
            return
    }

    guard let data = try? Data(contentsOf: jsonData) else { return }

    guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else{return}

    if let dictionary = json as? [String: Any] {

        if let title = dictionary["title"] as? String {
            print("in title")
            labelHeader.text = title
        }

        if let data = dictionary["data"] as? Any {
            print("data is \(data)")
        }
        if let date = dictionary["date"] as? Date {
            print("date is \(date)")
        }
        // And so on

        for (key, value) in dictionary {
            print("Key is: \(key) and value is \(value)" )
            //This print the whole JSON to the console.
        }
    }

    //Now lets populate our TableView
    let newUrl = Bundle.main.url(forResource: "channels", withExtension: "json")

    guard let j = newUrl
        else{
            print("data not found")
            return
    }

    guard let d = try? Data(contentsOf: j)
        else { print("failed")
            return
    }

    guard let rootJSON = try? JSONSerialization.jsonObject(with: d, options: [])
        else{ print("failedh")
            return
    }

    if let JSON = rootJSON as? [String: Any] {
        labelHeader.text = JSON["id"] as? String //Should update the Label in the ListView with the ID found in the JSON

        guard let jsonArray = JSON["type"] as? [[String: Any]] else {
            return
        }

        let name = jsonArray[0]["name"] as? String
        print(name ?? "NA")
        print(jsonArray.last!["date"] as? Int ?? 1970)

        channelList = jsonArray.compactMap{return channelData($0)}

        self.tableView.reloadData()

    }
}

这是 JSON 文件的示例:

{
"format": "json",
"data": [
    {
        "type": "channel",
        "id": "123",
        "updated_at": "2019-05-03 11:32:57",
        "context": "search",
        "relationships": {
            "recipients": [
                {
                    "type": "user",
                    "id": 321,
                    "participant_id": 456
                }
            ],
            "search": {
                "type": "search",
                "title": "Title"
            },
        }
    },

我想找到使用这种 JSON 的最佳方式。

目前我无法将数据获取到 listView。我拥有的最多的是我在 xCode 控制台中的 JSON(至少这意味着我能够打开 JSON)。

【问题讨论】:

  • 在准备好回答这个问题之前,您还有很长的路要走。请参阅JSONSerialization 课程以了解开始的地方。还有一些用于处理 JSON 数据的库。
  • 解析 JSON 是最常见的问题之一。有more than 4000 related questions
  • 我不想发布我的整个代码,但我会用更多代码更新问题。 @vadian 问题是我发现可能与我的案例有关的问题已经过时(3 年或更长时间)。
  • 你没有意识到你的 JSON 不包含像 namedate 这样的键吗?请学习阅读 JSON,这真的很容易。我写了一个快速概述here
  • @vadian 不,我没有。我是一个使用 JSON 的菜鸟,并且一年多没有使用 Swift,所以我生疏了。我会看看你的答案,谢谢。

标签: json swift uitableview parsing


【解决方案1】:

请将此作为您当前开发的基本代码。我使用了 Structs,它可以帮助您保持 JSON 模型的顺序,并对其进行正确编码,并在将来将其用作对象。

结构

// http request response results iTunes Site.
struct SearchResult: Decodable {
    let resultCount: Int
    let results: [Result]
}

// Raw Result
struct Result: Decodable {
    let trackId: Int
    let artistId: Int
    let artistName: String
    let collectionName: String
    let trackName: String
    let artworkUrl30: String
    let artworkUrl60: String
    let artworkUrl100: String
    let primaryGenreName: String
    let trackPrice: Float
    let collectionPrice: Float
    let trackTimeMillis: Int

}

功能代码

func fetchArtists(searchTerm: String, completion: @escaping (SearchResult?, Error?) -> ()) {
        let escapedString = searchTerm.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
        let urlString = "https://itunes.apple.com/search?term=\(escapedString)&entity=musicTrack"
        fetchGenericJSONData(urlString: urlString, completion: completion)
    }

    func fetchGenericJSONData<T: Decodable>(urlString: String, completion: @escaping (T?, Error?) -> ()) {

        guard let url = URL(string: urlString) else { return }
        URLSession.shared.dataTask(with: url) { (data, resp, err) in
            if let err = err {
                completion(nil, err)
                return
            }
            do {
                let objects = try JSONDecoder().decode(T.self, from: data!)
                completion(objects, nil)
            } catch {
                completion(nil, error)
            }
            }.resume()
    }

请求代码如何使用它。

fetchArtists(searchTerm: searchText) { res, err in
    if let err = err {
        print("Failed to fetch artists:", err)
        return
    }
    self.iTunesResults = res?.results ?? []
    print(self.iTunesResults.artistName)
    // Uncomment this in case you have a tableview to refresh
    // DispatchQueue.main.async {
    //     self.tableView.reloadData()
    // }
}

【讨论】:

    【解决方案2】:

    在 Swift 4+ 中解析 JSON 的推荐方法是 Codable 协议。

    创建结构

    struct Root: Decodable {
        let format: String
        let data: [ChannelData]
    }
    
    struct ChannelData: Decodable {
        let type, id, updatedAt, context: String
        let relationships: Relationships
    }
    
    struct Relationships: Decodable {
        let recipients: [Recipient]
        let search: Search
    }
    
    struct Recipient: Decodable {
        let type: String
        let id: Int
        let participantId: Int
    }
    
    struct Search: Decodable {
        let type: String
        let title: String
    }
    

    由于channels.json 文件在应用程序包中并且无法修改,您可以将viewDidLoad 减少到

    var channelList = [ChannelData]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let url = Bundle.main.url(forResource: "channels", withExtension: "json")!
        let data = try! Data(contentsOf: url)
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        let result = try! decoder.decode(Root.self, from: data)
        channelList = result.data
        self.tableView.reloadData()
    }
    

    如果代码崩溃,则表明存在设计错误。结构与问题中的 JSON 匹配。可能它要大得多,那么你必须调整或扩展结构。

    【讨论】:

    • 我更清楚地了解它是如何工作的。我尝试了它并使其适应我的代码,但出现错误:Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "unread_count", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"unread_count\", intValue: nil) (\"unread_count\").", underlyingError: nil))
    • 问题中没有键 unread_count。发布伪代码是没有意义的。
    • 我在 ChannelData 中添加了它(JSON 中有一个)struct ChannelData: Decodable { let type, id, updatedAt, context: String let archived: Bool let unread_count: Int let relationships: Relationships } unread_count 不在嵌套数组中,我能够在将代码更改为您的代码之前得到它。
    • 注意密钥解码策略.convertFromSnakeCase。将 JSON 键 updated_at 与相应的结构成员进行比较。有什么区别?
    • 我明白了。谢谢。我不知道大写和小写对于变量来说可能是这样的问题。谢谢。我真的很抱歉,但我仍然无法访问“评级”,经过几个小时的试用,它让我发疯......我将 let rating: Double 添加到 Struct Recipient (这就是它在 JSON 中的位置) .我不知道如何从我的代码中访问它。我尝试了xxxx.relationships.recipients.something,但点击 .我试图创建另一个解码器,如你的例子,但没有成功......我真的很抱歉打扰你,我发誓我正在尝试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多