Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Json Swift Codable:解码具有相同根对象的不同项目数组_Json_Swift_Swift4_Codable_Decodable - Fatal编程技术网

Json Swift Codable:解码具有相同根对象的不同项目数组

Json Swift Codable:解码具有相同根对象的不同项目数组,json,swift,swift4,codable,decodable,Json,Swift,Swift4,Codable,Decodable,我目前正在尝试解码JSON,如下所示: { “结果”:{ “成功”:没错, “项目”:[ { “timeEntryID”:“1”, “开始”:“1519558200”, “结束”:“1519563600”, “客户名称”:“测试客户”, “项目名称”:“测试项目”, “说明”:“条目1”, }, { “timeEntryID”:“2”, “开始”:“1519558200”, “结束”:“1519563600”, “客户名称”:“测试客户”, “项目名称”:“测试项目”, “说明”:“条目2”,

我目前正在尝试解码JSON,如下所示:

{
“结果”:{
“成功”:没错,
“项目”:[
{
“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这是决定性的一点!这么简单。谢谢你和瓦迪安。我真的很感激你的回答!我就是这样做的。仿制药摇滚!