简体   繁体   English

在Swift 4和Xcode 9中解码ExpandableTableView的嵌套json数组

[英]Decoding nested json array for ExpandableTableView in swift 4 and Xcode 9

I'm new to iOS and making an ExpandableTableView with JSON data. 我是iOS的新手,使用JSON数据制作了ExpandableTableView The data is in form of nested arrays. 数据采用嵌套数组的形式。 I want to set data of parent array as TableView header and data of child array as ExpandableTable elements. 我想将父数组的数据设置为TableView标头,并将子数组的数据设置为ExpandableTable元素。 I'm performing JSONDecoding through struct and enums and storing the response in DataModelClass . 我正在通过structenums执行JSONDecoding并将响应存储在DataModelClass中 While Decoding it's throwing error 解码时会引发错误

typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))

that is ok because I know that I'm missing something very small. 可以,因为我知道我缺少了一些很小的东西。 I've gone through it many times but I'm unable to understand the problem. 我已经经历了很多次,但我无法理解问题。

There are many similar types of questions on stack but none of then are matching my criteria. 堆栈上有许多类似类型的问题,但没有一个符合我的标准。 Please help me out. 请帮帮我。

My JSON is 我的JSON是

    [
    {
        "category_id": "1",
        "category_name": "ROLL",
        "category_start_time": "10:00:00",
        "category_end_time": "22:59:59",
        "data": [
            {
                "id": "301",
                "item_code": null,
                "item_name": "CHICKN ROLL",
                "main_item_id": "1",
                "item_price": null,
                "tax_id": "0",
                "tax_per": "0",
                "tax_amount": "0",
                "item_net_price": "180",
                "sub_item_price": "0",
                "sub_tax_amount": "0",
                "sub_item_net_price": "0",
                "created_date": "2018-02-28 12:20:53",
                "created_by": null,
                "image_name": "",
                "image_path": null,
                "quantity": null,
                "notify_quant": null,
                "active": "1",
                "today_avilable": "1",
                "kot": "1",
                "counter_id": "0",
                "store_id": "1"
            }
        ]
    }
]

and the Model class is 而Model类是

    import Foundation

class AllStoreItems {
    var category_id: String
    var category_name: String
    var category_start_time: String
    var category_end_time: String
    var data: [data]
    var expanded: Bool

    init(category_id: String, category_name: String, category_start_time: String, category_end_time: String, data: [data], expanded: Bool = false) {
        self.category_id = category_id
        self.category_name = category_name
        self.category_start_time = category_start_time
        self.category_end_time = category_end_time
        self.data = data
        self.expanded = expanded
    }

}

class ItemsData {
    var id: String
    var item_code: String
    var item_name: String
    var main_item_id: String
    var item_price: String
    var tax_id: String
    var tax_per: String
    var tax_amount: String
    var item_net_price: String
    var sub_item_price: String
    var sub_tax_amount: String
    var sub_item_net_price: String
    var created_date: String
    var created_by: String
    var image_name: String
    var image_path: String
    var quantity: String
    var notify_quant: String
    var active: String
    var today_avilable: String
    var kot: String
    var counter_id: String
    var store_id: String

    init(id: String, item_code: String, item_name: String, main_item_id: String, item_price: String, tax_id: String, tax_per: String, tax_amount: String, item_net_price: String, sub_item_price: String, sub_tax_amount: String, sub_item_net_price: String, created_date: String, created_by: String, image_name: String, image_path: String, quantity: String, notify_quant: String, active: String, today_avilable: String, kot: String, counter_id: String, store_id: String) {
        self.id = id
        self.item_code = item_code
        self.item_name = item_name
        self.main_item_id = main_item_id
        self.item_price = item_price
        self.tax_id = tax_id
        self.tax_per = tax_per
        self.tax_amount = tax_amount
        self.item_net_price = item_net_price
        self.sub_item_price = sub_item_price
        self.sub_tax_amount = sub_tax_amount
        self.sub_item_net_price = sub_item_net_price
        self.created_date = created_date
        self.created_by = created_by
        self.image_name = image_name
        self.image_path = image_path
        self.quantity = quantity
        self.notify_quant = notify_quant
        self.active = active
        self.today_avilable = today_avilable
        self.kot = kot
        self.counter_id = counter_id
        self.store_id = store_id
    }
}

My struct and enums are 我的结构和枚举是

import Foundation

struct AllItem: Decodable {
    var category_id: String
    var category_name: String
    var category_start_time: String
    var category_end_time: String
    var data: [data]

    enum AllItem: String {
        case category_id = "category_id"
        case category_name = "category_name"
        case category_start_time = "category_start_time"
        case category_end_time = "category_end_time"
        case data = "data"
    }
}

struct data: Decodable {
    var id: String
    var item_code: String
    var item_name: String
    var main_item_id: String
    var item_price: String
    var tax_id: String
    var tax_per: String
    var tax_amount: String
    var item_net_price: String
    var sub_item_price: String
    var sub_tax_amount: String
    var sub_item_net_price: String
    var created_date: String
    var created_by: String
    var image_name: String
    var image_path: String
    var quantity: String
    var notify_quant: String
    var active: String
    var today_avilable: String
    var kot: String
    var counter_id: String
    var store_id: String

    enum data: String {
        case id = "id"
        case item_code = "item_code"
        case item_name = "item_name"
        case main_item_id = "main_item_id"
        case item_price = "item_price"
        case tax_id = "tax_id"
        case tax_per = "tax_per"
        case tax_amount = "tax_amount"
        case item_net_price = "item_net_price"
        case sub_item_price = "sub_item_price"
        case sub_tax_amount = "sub_tax_amount"
        case sub_item_net_price = "sub_item_net_price"
        case created_date = "created_date"
        case created_by = "created_by"
        case image_name = "image_name"
        case image_path = "image_path"
        case quantity = "quantity"
        case notify_quant = "notify_quant"
        case active = "active"
        case today_avilable = "today_avilable"
        case kot = "kot"
        case counter_id = "counter_id"
        case store_id = "store_id"
    }

}

and my ViewController is 而我的ViewController是

class CustomItemTableView: UITableViewCell {

    @IBOutlet weak var textLabelOne: UILabel!
    @IBOutlet weak var textLabelTwo: UILabel!

}

class AllItemsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, AllItemsHeaderViewDelegate {

    @IBOutlet weak var headerStoreLogoIV: UIImageView!
    @IBOutlet weak var tableView: UITableView!

    var storeId: String = ""
    var storeCatId: String = ""
    var storeName: String = ""
    var storeLogoLink: String = ""

    @IBOutlet weak var storeImage: UIImageView!
    @IBOutlet weak var storeNameLabel: UILabel!


    let mainUrl = BaseURL()

    var items = [AllStoreItems]()
    var subItems = [ItemsData]()

    override func viewDidLoad() {
        super.viewDidLoad()
        ExpandItemsApi()
        let imgUrl: String = mainUrl.MainUrl + "logo/" + storeLogoLink
        let imgUrlStr: String = imgUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
        //let imgUrl2: URL = URL(string: imgUrlStr)!

        //let imgUrl3 = URLRequest(url: imgUrl2!)

        storeNameLabel.text = storeName
//        storeNames.text = storeName
//        storeCategory.text = storeCatId
//        storeIds.text = storeId
//        imgLink.text = storeLogoLink

        storeImage.downloadedFrom(link: imgUrlStr)

    }

    @IBAction func backButton(_ sender: Any) {
        self.dismiss(animated: false, completion: nil)
    }

    func ExpandItemsApi() {

        let myActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)

        myActivityIndicator.center = view.center
        myActivityIndicator.hidesWhenStopped = false
        myActivityIndicator.startAnimating()
        view.addSubview(myActivityIndicator)

        let itemsUrl = URL(string: mainUrl.MainUrl + "viewmaincat")
        var itemUrls = URLRequest(url: itemsUrl!)
        itemUrls.httpMethod = "POST"

        itemUrls.addValue("application/json", forHTTPHeaderField: "content-type")
        itemUrls.addValue("application/json", forHTTPHeaderField: "Accept")

        let storeDetails = ["store_id": storeId] as [String: String]

        do {
            itemUrls.httpBody = try JSONSerialization.data(withJSONObject: storeDetails, options: .prettyPrinted)
        }catch let error {
            toastNeck(message: "\(error)")
            myActivityIndicator.stopAnimating()
            myActivityIndicator.hidesWhenStopped = true
        }

        URLSession.shared.dataTask(with: itemUrls) {
            (datas, response, error) in
            if datas != nil {
                do {
                    let itemDetails = try JSONDecoder().decode(AllItem.self, from: datas!)

                    self.items.append(AllStoreItems(category_id: itemDetails.category_id, category_name: itemDetails.category_name, category_start_time: itemDetails.category_start_time, category_end_time: itemDetails.category_end_time, data: itemDetails.data))

                    print(itemDetails.data)
//                   let subItem = try JSONDecoder().decode(data.self, from: datas!)
//                   self.subItems.append(ItemsData(id: subItem.id, item_code: subItem.item_code, item_name: subItem.item_name, main_item_id: subItem.main_item_id, item_price: subItem.item_price, tax_id: subItem.tax_id, tax_per: subItem.tax_per, tax_amount: subItem.tax_amount, item_net_price: subItem.item_net_price, sub_item_price: subItem.sub_item_price, sub_tax_amount: subItem.sub_tax_amount, sub_item_net_price: subItem.sub_item_net_price, created_date: subItem.created_date, created_by: subItem.created_by, image_name: subItem.image_name, image_path: subItem.image_path, quantity: subItem.quantity, notify_quant: subItem.notify_quant, active: subItem.active, today_avilable: subItem.today_avilable, kot: subItem.kot, counter_id: subItem.counter_id, store_id: subItem.store_id))
                }catch let errors {
                    self.toastNeck(message: "\(errors)")
                    print(errors)
                    DispatchQueue.main.async{
                        myActivityIndicator.stopAnimating()
                        myActivityIndicator.hidesWhenStopped = true
                    }
            }
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            }
        }.resume()

    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return items.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items[section].data.count
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 50
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if items[indexPath.section].expanded {
            return 50
        }else {
            return 0
        }
    }

    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return 5
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header = AllItemsHeaderView()
        header.customInit(title: items[section].category_name, section: section, delegate: self)
        return header
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: CustomItemTableView = tableView.dequeueReusableCell(withIdentifier: "customItems") as! CustomItemTableView
        cell.textLabelOne.text = items[indexPath.section].data[indexPath.row].item_name
        cell.textLabelTwo.text = items[indexPath.section].data[indexPath.row].item_price
        return cell
    }

    func toggleSection(header: AllItemsHeaderView, section: Int) {
        items[section].expanded = !items[section].expanded
        tableView.beginUpdates()

        for i in 0 ..< items[section].data.count {
            tableView.reloadRows(at: [IndexPath(row: i, section: section)], with: .automatic)
        }
        tableView.endUpdates()
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        toastNeck(message: items[indexPath.section].data[indexPath.row].item_name)
    }

    func toastNeck(message: String) {
        DispatchQueue.main.async {
            let toastLabel = UILabel(frame: CGRect(x: self.view.frame.size.width/2-100, y: self.view.frame.size.height/2, width: 200, height: 40))
            toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.4)
            toastLabel.textColor = UIColor.white
            toastLabel.textAlignment = NSTextAlignment.center
            toastLabel.text = message
            toastLabel.layer.cornerRadius = 15
            toastLabel.clipsToBounds = true
            self.view.addSubview(toastLabel)
            UIView.animate(withDuration: 8, animations: {
                toastLabel.alpha = 0}, completion: {(isCompleted) in toastLabel.removeFromSuperview()})
        }
    }
}

if anyone need anything more, please comment... 如果还有其他需要,请发表评论...

Try changing these two lines: 尝试更改这两行:

let itemDetails = try JSONDecoder().decode(AllItem.self, from: datas!)

self.items.append(AllStoreItems(category_id: itemDetails.category_id, category_name: itemDetails.category_name, category_start_time: itemDetails.category_start_time, category_end_time: itemDetails.category_end_time, data: itemDetails.data))

to

let itemDetails = try JSONDecoder().decode([AllItem].self, from: datas!)

for item in itemDetails {
     self.items.append(AllStoreItems(category_id: item.category_id, category_name: item.category_name, category_start_time: item.category_start_time, category_end_time: item.category_end_time, data: item.data))
}

And also there is no need of the enums AllItem and data as the property names are already same as JSON keys. 并且也不需要枚举AllItemdata因为属性名称已经与JSON键相同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM