Swift 4 Decodable:来自嵌套数组的struct

Bry*_*han 7 json swift codable decodable

鉴于以下JSON文档,我想创建一个struct包含四个属性:filmCount(Int),year(Int),category(String)和actor(Actor数组).

{    
    "filmCount": 5,
    "year": 2018,
    "category": "Other",
    "actors":{  
        "nodes":[  
            {  
                "actor":{  
                    "id":0,
                    "name":"Daniel Craig"
                }
            },
            {  
                "actor":{  
                    "id":1,
                    "name":"Naomie Harris"
                }
            },
            {  
                "actor":{  
                    "id":2,
                    "name":"Rowan Atkinson"
                }
            }
        ]
    }
}
Run Code Online (Sandbox Code Playgroud)

PlacerholderData是一个存储三个主要属性和actor列表的结构,这些属性应该从JSON对象nodes的actors属性中的嵌套容器中检索.

PlacerholderData:

struct PlaceholderData: Codable {
    let filmCount: Int
    let year: Int
    let category: String
    let actors: [Actor]
}
Run Code Online (Sandbox Code Playgroud)

Actor.swift:

struct Actor: Codable {
    let id: Int
    let name: String
}
Run Code Online (Sandbox Code Playgroud)

我试图通过提供我自己init的手动初始化解码器容器中的值来做到这一点.如何在不必拥有存储nodes对象的中间结构的情况下解决此问题?

San*_*eep 5

您可以使用 nestedContainer(keyedBy :)和nestedUnkeyedContainer(forKey :)这样对嵌套数组和字典进行解码,以将其转换为所需的结构。您在init(decoder:)中的解码可能看起来像这样,

用于解码的演员扩展,

extension Actor: Decodable {

    enum CodingKeys: CodingKey { case id, name }

    enum ActorKey: CodingKey { case actor }

    init(from decoder: Decoder) throws {
        let rootKeys        = try decoder.container(keyedBy: ActorKey.self)
        let actorContainer  = try rootKeys.nestedContainer(keyedBy: CodingKeys.self,
                                                           forKey: .actor)
        try id =  actorContainer.decode(Int.self,
                                       forKey: .id)
        try name =  actorContainer.decode(String.self,
                                         forKey: .name)
    }
}
Run Code Online (Sandbox Code Playgroud)

用于解码的PlaceholderData扩展,

extension PlaceholderData: Decodable {

    enum CodingKeys: CodingKey { case filmCount, year, category, actors }

    enum NodeKeys: CodingKey { case nodes }

    init(from decoder: Decoder) throws {
        let rootContainer   = try decoder.container(keyedBy: CodingKeys.self)
        try filmCount       =  rootContainer.decode(Int.self,
                                                    forKey: .filmCount)
        try year            =  rootContainer.decode(Int.self,
                                                    forKey: .year)
        try category        =  rootContainer.decode(String.self,
                                                    forKey: .category)
        let actorsNode      = try rootContainer.nestedContainer(keyedBy: NodeKeys.self,
                                                                forKey: .actors)
        var nodes = try actorsNode.nestedUnkeyedContainer(forKey: .nodes)
        var allActors: [Actor] = []

        while !nodes.isAtEnd {
            let actor = try nodes.decode(Actor.self)
            allActors += [actor]
        }
        actors = allActors
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以像这样解码

let decoder = JSONDecoder()
do {
    let placeholder = try decoder.decode(PlaceholderData.self, from: jsonData)
    print(placeholder)
} catch {
    print(error)
}
Run Code Online (Sandbox Code Playgroud)

在这里,基本思想是使用nestedContainer(keyedBy :)解码字典容器,并使用nestedUnkeyedContainer(forKey :)解码数组容器。