如何使用jsonDecoder处理来自JSON响应的动态密钥?

时间:2019-02-25 12:26:35

标签: ios json swift decode codable

如何在Swift 4中使用解码器处理此JSON并解析此JSON?我尝试了几次,但是失败了。我不明白如何处理此JSON格式。

[
  {
    products: {
      id: 69,
      name: "test",
      des: "Hi this is a test",
      sort_des: "this is a test category",
    },
    documents: {
      0: {
        id: 1,
        name: "105gg_1992uu",
        citation: "This is citation for 105gg_1992uu",
        file: "105gg_1992uu.pdf",

       created_at: "2019-01-25 09:07:09",
        category_id: 69,
      },
      1: {
        id: 2,
        name: "96tt-1997tt",
        citation: "This is citation for 96tt-1997tt",
        file: "96tt-1997tt.pdf",
        created_at: "2019-01-25 09:07:09",
        category_id: 69,
      },

    },

  }
]

我尝试了以下代码。 这是我的模型课。

struct GenericCodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(stringValue: String) {
        self.stringValue = stringValue
    }
    init?(intValue: Int) {
        self.intValue = intValue
        self.stringValue = "\(intValue)"
    }
}

struct Model : Codable {

  struct Documents : Codable {
        var id : Int?
        var name : String?
        var citation : String?
        var file : String?
        var created_at : String?
        var category_id : Int?## Heading ##

     private enum CodingKeys : String, CodingKey{
            case id = "id"
            case name =  "name"
            case citation = "citation"
            case file = "file"
            case created_at = "created_at"
            case category_id = "category_id"
      }
   }

   struct Products : Codable {
        var id : Int?
        var name : String?
        var des : String?
        var sort_des : String?

      private enum CodingKeys: String, CodingKey{
            case id = "id"
            case name = "name"
            case des = "des"
            case sort_des = "sort_des"
      }
  }

    var products : Products?
    var documents : [Documents]?
    private enum CodingKeys : String, CodingKey{
        case products
        case documents
    }
    init(from decoder: Decoder) throws {
        self.documents = [Documents]()
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.products = try container.decode(Products.self, forKey: .products)
        let documents = try container.nestedContainer(keyedBy: GenericCodingKeys.self, forKey: .documents)
        for doc in documents.allKeys{
            let docEach = try documents.decode(Documents.self, forKey: doc)
            self.documents?.append(docEach)
        }
    }
}

这是我从JSON函数中获取数据

   class LatestStatuesVC: UIViewController,UITableViewDelegate,UITableViewDataSource {

    @IBOutlet var tableView: UITableView!
    var caseData : [Model]?
    var model : Model?
    var countCaseData = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        downloadAllData()
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return countCaseData
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: ReuseIdentifiers.cellLatestStatues, for: indexPath) as! LatestStatuesTableCell
        return cell
    }

    //MARK: Download all documents into internal directory
    func downloadAllData(){
        let url = URL(string: URLString.urlForGetDocuments)
        URLSession.shared.dataTask(with: url!) { (data, response, err) in
            DispatchQueue.main.async {
                do{
                    if err == nil {
                        let products = try JSONDecoder().decode(Model.Products.self, from: data!)
                        let documentAll = try JSONDecoder().decode([Model.Documents].self, from: data!)
                        print(products.name as Any)
                        self.countCaseData = documentAll.count
                        for doc in documentAll{
                            print(doc.name as Any)
                            print(doc.citation as Any)
                        }
                    }
                }
                catch let err{
                    print(err)
                }
            }
        }.resume()
    }

}

我收到此代码的错误。

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

1 个答案:

答案 0 :(得分:0)

该错误清楚地表明JSON的根对象是一个数组,但是您尝试对字典进行解码。

基本上,您的结构太复杂了。如果要通过摆脱数字字典键来将documents作为数组,只需在根(Model)结构中编写一个自定义初始化程序即可将documents解码为字典并获取值按id排序为documents数组。

struct Model : Decodable {
    let products : Product
    let documents : [Document]

    enum CodingKeys: String, CodingKey { case products, documents }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        products = try container.decode(Product.self, forKey: .products)
        let documentData = try container.decode([String:Document].self, forKey: .documents)
        documents = documentData.values.sorted(by: {$0.id < $1.id})
    }
}

struct Product: Decodable {
    let id : Int
    let name, description, sortDescription : String
    let type : String
}

struct Document: Decodable {
    let id, categoryId : Int
    let name, citation, file : String
    let createdAt : Date
}

然后解码JSON(假设data将JSON表示为Data),将createdAt的值解码为Date

let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
decoder.dateDecodingStrategy = .formatted(dateFormatter)
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
    let modelArray = try decoder.decode([Model].self, from: data)
    for model in modelArray {
        print("products:",model.products)
        print("documents:",model.documents)
    }

} catch { print(error) }

convertFromSnakeCase密钥解码策略将所有 snake_cased 密钥转换为 camelCased 结构成员,而无需指定任何CodingKeys