'Swift parsing JSON into Table View doesnt work

Hi I have problems to read the Json Data into the TableView. Can anybody help me, its my first time working with it. I know I am doing something wrong, but I cant finde the Solution for my JSON File cause in the Internet they use simple ones..

Here is my JSON:

{
    "data":[
        {
            "title": "Brot",
            "desc":[
                {
                    "Name": "Roggenschrot- und Roggenvollkornbrot",
                    "Menge": "Gramm",
                    "Kalorie": "2",
                    "Energiedichte": "Gelb"
                },
                {
                    "Name": "Weizenschrot- und Weizenvollkornbrot",
                    "Menge": "Gramm",
                    "Kalorie": "2",
                    "Energiedichte": "Gelb"
                },
                {
                    "Name": "Weizenschrot- und Weizenvollkornbrot",
                    "Menge": "Gramm",
                    "Kalorie": "2",
                    "Energiedichte": "Gelb"
                },
            ]
        }
    ]
}

Here is my tableView

import UIKit

class EseenTagebuchTableViewController: UITableViewController {
    
    var result: Result?
    var resultItem: ResultItem?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        parseJSON()

    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return result?.data.count ?? 0
    }
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return result?.data[section].title
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        if let result = result {
            return result.data.count
        }
        return 0
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let text = resultItem?.desc?[indexPath.section].Name?[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = text
        return cell
    }
    

    private func parseJSON() {
        guard let path = Bundle.main.path(forResource: "Ernahrungstagebuch", ofType: "json") else {
            return
        }
        let url = URL(fileURLWithPath: path)
        
       
        
        do {
            let jsonData = try Data(contentsOf: url)
            result = try JSONDecoder().decode(Result.self, from: jsonData)
            return
        }
        catch {
            print("Error: \(error)")
        }
    }

}

And here are my Model 1 and after my Model 2

import Foundation

struct Result: Codable {
    let data: [ResultItem]
   
}

struct ResultItem: Codable {
    let title: String
    let desc: [descItems]?
}

Model 2

import Foundation

struct descItems: Codable {
    let Name: String?
    let Menge: String?
    let Kalorie: Int?
    let Energiedichte: String?
}

What am I doing wrong?



Solution 1:[1]

First your json data in your file is not correct, there is an extra "," comma after the last "Energiedichte": "Gelb" , remove that from your file.

Second your model is not correct, you should have:

struct Result: Codable {
    let data: [ResultItem]
}

struct ResultItem: Codable {
    let title: String
    let desc: [DescItems]?
}

struct DescItems: Codable {
    let Name: String?
    let Menge: String?
    let Kalorie: String?  // <-- here not Int?
    let Energiedichte: String?
}

Using your code parseJSON() I was able to parse the data without errors.

EDIT-1: if you really want kalories to be Int, try:

struct DescItems: Codable {
    let name: String?
    let menge: String?
    let kalorie: Int?  // <-- here
    let energiedichte: String?
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        menge = try container.decode(String.self, forKey: .menge)
        energiedichte = try container.decode(String.self, forKey: .energiedichte)
        let calorie = try container.decode(String.self, forKey: .kalorie)  // <-- here
        kalorie = Int(calorie) // <-- here
    }
    
    enum CodingKeys: String, CodingKey {
        case name = "Name"
        case menge = "Menge"
        case kalorie = "Kalorie"
        case energiedichte = "Energiedichte"
    }
}

Note the change of case, as per common practice.

Solution 2:[2]

you can also use json parse below code:

func parseJSON() {
let path =  Bundle.main.path(forResource: "Ernahrungstagebuch", ofType: "json")
let jsonData = try? NSData(contentsOfFile: path!, options: NSData.ReadingOptions.mappedIfSafe)
let jsonResult: NSDictionary = try! (JSONSerialization.jsonObject(with: jsonData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary)!

if let data = jsonResult["data"] as? [[String:Any]] {
    self.arrPlan = data.map{Day(JSON: $0)!}
    //            print(arrPlan)
    
}

}

Try to this code for get data array object and after reload tableview Data.

Solution 3:[3]

You can use this extension to parse json.

import Foundation

extension Bundle {
    func decode<T: Decodable>(_ type: T.Type, from file: String, dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> T {
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle.")
        }

        guard let data = try? Data(contentsOf: url) else {
            fatalError("Failed to load \(file) from bundle.")
        }

        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = dateDecodingStrategy
        decoder.keyDecodingStrategy = keyDecodingStrategy

        do {
            return try decoder.decode(T.self, from: data)
        } catch DecodingError.keyNotFound(let key, let context) {
            fatalError("Failed to decode \(file) from bundle due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
        } catch DecodingError.typeMismatch(_, let context) {
            fatalError("Failed to decode \(file) from bundle due to type mismatch – \(context.debugDescription)")
        } catch DecodingError.valueNotFound(let type, let context) {
            fatalError("Failed to decode \(file) from bundle due to missing \(type) value – \(context.debugDescription)")
        } catch DecodingError.dataCorrupted(_) {
            fatalError("Failed to decode \(file) from bundle because it appears to be invalid JSON")
        } catch {
            fatalError("Failed to decode \(file) from bundle: \(error.localizedDescription)")
        }
    }
}

And use it like this:

let incomingData = Bundle.main.decode([Your_Model].self, from: "Your_Json_File_name.json")

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Amreliya Dipak
Solution 3 Habin Lama