如何使用 jsonDecoder 处理来自 JSON 响应的动态键?
Posted
技术标签:
【中文标题】如何使用 jsonDecoder 处理来自 JSON 响应的动态键?【英文标题】:How to handle dynamic keys from a JSON response using jsonDecoder? 【发布时间】:2019-02-25 12:26:34 【问题描述】:如何处理这个 JSON 并在 Swift 4 中使用解码器解析这个 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))
【问题讨论】:
您是否收到任何错误消息?你得到什么结果? 是的。我得到“typeMismatch(Swift.Dictionary[
开头,因此根对象显然是一个数组。这就是错误消息的内容。
【参考方案1】:
错误清楚地表明 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
。
【讨论】:
为什么使用“区域设置”。你能用几行详细说明你的代码吗? @vadian 顺便说一句,感谢您的评论。我认为它可以帮助我。但是当我编译您的代码时,会显示此错误:“valueNotFound(Swift.KeyedDecodingContainer(unknown context at 0x10c2d86c8).CodingKeys>, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue:”Index 0 ", intValue: 0), CodingKeys(stringValue: "documents", intValue: nil), _DictionaryCodingKey(stringValue: "3876", intValue: 3876)], debugDescription: "无法获取键控解码容器 -- 改为找到空值。" , 底层错误: nil)) " 固定区域设置用于避免在格式化日期时出现意外行为。我的代码与您的相似,但更简单。 但是你的代码没有成功运行。出现上述错误。 我的答案是针对问题中的 JSON。如果您想要一个可行的解决方案,请发布 real JSON以上是关于如何使用 jsonDecoder 处理来自 JSON 响应的动态键?的主要内容,如果未能解决你的问题,请参考以下文章
如何准备 API 响应以在 swift 中与 jsonDecoder 一起使用
Swift 项目中涉及到 JSONDecoder,网络请求,泛型协议式编程的一些记录和想法
Python 3 中的 JSONdecoder 错误。来自 API 的 Json