繁体   English   中英

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

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

我是iOS的新手,使用JSON数据制作了ExpandableTableView 数据采用嵌套数组的形式。 我想将父数组的数据设置为TableView标头,并将子数组的数据设置为ExpandableTable元素。 我正在通过structenums执行JSONDecoding并将响应存储在DataModelClass中 解码时会引发错误

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

可以,因为我知道我缺少了一些很小的东西。 我已经经历了很多次,但我无法理解问题。

堆栈上有许多类似类型的问题,但没有一个符合我的标准。 请帮帮我。

我的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"
            }
        ]
    }
]

而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
    }
}

我的结构和枚举是

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"
    }

}

而我的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()})
        }
    }
}

如果还有其他需要,请发表评论...

尝试更改这两行:

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))

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))
}

并且也不需要枚举AllItemdata因为属性名称已经与JSON键相同。

暂无
暂无

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

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