Json Swift Codable:解码具有相同根对象的不同项目数组
我目前正在尝试解码JSON,如下所示:Json Swift Codable:解码具有相同根对象的不同项目数组,json,swift,swift4,codable,decodable,Json,Swift,Swift4,Codable,Decodable,我目前正在尝试解码JSON,如下所示: { “结果”:{ “成功”:没错, “项目”:[ { “timeEntryID”:“1”, “开始”:“1519558200”, “结束”:“1519563600”, “客户名称”:“测试客户”, “项目名称”:“测试项目”, “说明”:“条目1”, }, { “timeEntryID”:“2”, “开始”:“1519558200”, “结束”:“1519563600”, “客户名称”:“测试客户”, “项目名称”:“测试项目”, “说明”:“条目2”,
{
“结果”:{
“成功”:没错,
“项目”:[
{
“timeEntryID”:“1”,
“开始”:“1519558200”,
“结束”:“1519563600”,
“客户名称”:“测试客户”,
“项目名称”:“测试项目”,
“说明”:“条目1”,
},
{
“timeEntryID”:“2”,
“开始”:“1519558200”,
“结束”:“1519563600”,
“客户名称”:“测试客户”,
“项目名称”:“测试项目”,
“说明”:“条目2”,
}
],
“总计”:“2”
},
“id”:“1”
}
这种特定类型的JSON的解码过程非常简单。我只需要这样的东西:
struct ResponseKeys:可解码{
让结果:ResultKeys
结构结果键:可解码{
让成功:布尔
出租项目:[项目]
}
}
现在我面临的问题是,服务器的每个响应都具有与上述JSON相同的结构,但具有不同的项类型。因此,有时它是let items:[Item]
,但如果我调用用户端点,它也可能是let items:[User]
因为如果我只修改items数组就为每个端点编写上述swift代码,这将是不必要的代码重复,所以我创建了一个自定义解码器:
枚举KimaiapirResponseKeys:字符串,编码键{
案例结果
枚举KimaiResultKeys:字符串,编码键{
案例成功
案例项目
}
}
结构活动:可编码{
让id:Int
让描述:字符串?
让客户名称:String
让projectName:String
让我们开始日期:日期
让endDateTime:日期
枚举编码键:字符串,编码键{
案例id=“timeEntryID”
案例描述
案例客户名称
案例项目名称
案例startDateTime=“开始”
案例endDateTime=“结束”
}
}
推广活动{
init(来自解码器:解码器)抛出{
让resultContainer=try decoder.container(keyedBy:KimaiAPIResponseKeys.self)
let itemsContainer=try resultContainer.nestedContainer(keyedBy:KimaiAPIResponseKeys.KimaiResultKeys.self,forKey:.result)
让activityContainer=try itemsContainer.nestedContainer(keyedBy:Activity.CodingKeys.self,forKey:.items)
id=Int(尝试activityContainer.decode(String.self,forKey.id))!
description=尝试activityContainer.decodeIfPresent(String.self,forKey:.description)
customerName=尝试activityContainer.decode(String.self,forKey:.customerName)
projectName=尝试activityContainer.decode(String.self,forKey:.projectName)
startDateTime=Date(时间间隔自1970年起:双精度(尝试activityContainer.decode(String.self,forKey:.startDateTime))!)
endDateTime=Date(timeIntervalSince1970:Double(尝试activityContainer.decode(String.self,forKey:.endDateTime))!)
}
}
如果“items”
仅包含单个对象而不包含数组,则解码器工作正常:
{
“结果”:{
“成功”:没错,
“项目”:
{
“timeEntryID”:“2”,
“开始”:“1519558200”,
“结束”:“1519563600”,
“客户名称”:“测试客户”,
“项目名称”:“测试项目”,
“说明”:“条目2”,
},
“总计”:“2”
},
“id”:“1”
}
如果items
是一个数组,我会得到以下错误:
类型不匹配(Swift.Dictionary,Swift.DecodingError.Context(编码路径:[[uuu lldb_expr_151.kimaiapirresponsekeys.result],debugDescription:“应解码字典,但找到了数组。”,underyingerror:nil))
我就是不知道如何修改我的解码器来处理一组项目。我用JSON的工作版本和非工作版本创建了一个游乐场文件。请看一看并尝试一下:
谢谢你的帮助 我的建议是分别解码
项的词典
struct Item : Decodable {
enum CodingKeys: String, CodingKey {
case id = "timeEntryID"
case description, customerName, projectName
case startDateTime = "start"
case endDateTime = "end"
}
let id: Int
let startDateTime: Date
let endDateTime: Date
let customerName: String
let projectName: String
let description: String?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = Int(try container.decode(String.self, forKey: .id))!
description = try container.decodeIfPresent(String.self, forKey: .description)
customerName = try container.decode(String.self, forKey: .customerName)
projectName = try container.decode(String.self, forKey: .projectName)
startDateTime = Date(timeIntervalSince1970: Double(try container.decode(String.self, forKey: .startDateTime))!)
endDateTime = Date(timeIntervalSince1970: Double(try container.decode(String.self, forKey: .endDateTime))!)
}
}
在Activity
中,使用条件初始值设定项,它提供自己的do-catch
块。首先,它尝试解码单个项,并将单个项作为数组分配给属性。如果失败,它将解码一个数组
enum KimaiAPIResponseKeys: String, CodingKey {
case result, id
enum KimaiResultKeys: String, CodingKey {
case success
case items
}
}
struct Activity: Decodable {
let id: String
let items: [Item]
}
extension Activity {
init(from decoder: Decoder) throws {
let rootContainer = try decoder.container(keyedBy: KimaiAPIResponseKeys.self)
id = try rootContainer.decode(String.self, forKey: .id)
let resultContainer = try rootContainer.nestedContainer(keyedBy: KimaiAPIResponseKeys.KimaiResultKeys.self, forKey: .result)
do {
let item = try resultContainer.decode(Item.self, forKey: .items)
items = [item]
} catch {
items = try resultContainer.decode([Item].self, forKey: .items)
}
}
}
您可以使用泛型,这是处理这种情况的好方法
struct MainClass<T: Codable>: Codable {
let result: Result<T>
let id: String
}
struct Result <T: Codable>: Codable {
let success: Bool
let items: [T]
let total: String
}
struct MainClass:Codable{
让结果:结果
let id:String
}
结构结果:可编码{
让成功:布尔
出租项目:[T]
总数:字符串
}
在这里你可以得到这些物品
let data = Data()
let decoder = JSONDecoder()
let modelObjet = try! decoder.decode(MainClass<User>.self, from: data)
let users = modelObjet.result.items
let data=data()
let decoder=JSONDecoder()
让modelObjet=试试!decoder.decode(MainClass.self,from:data)
让用户=modelObjet.result.items
在我看来,泛型是处理这种情况下代码重复的最佳方法。感谢您的快速回答!我想你是对的,我需要为这些项目分别编写一个自定义解码器。如果我要实现你的答案,我可以解码JSON,它有一个项目或一个项目数组,但我总是有一个可以包含不同类型的数组。例如用户/活动。“项目”只是这些类型的通用术语。既然我需要在“let items:[here]”中指定类型,那么我该如何完成这个行为呢?只需将活动设置为泛型:结构活动
,它也会起同样的作用。@RobNapier这是决定性的一点!这么简单。谢谢你和瓦迪安。我真的很感激你的回答!我就是这样做的。仿制药摇滚!