Swift 4 可解码:来自嵌套数组的结构

Swift 4 Decodable: struct from nested array

鉴于以下 JSON 文档,我想创建一个具有四个属性的 structfilmCount (Int)、year (Int)、category(字符串)和 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"
                }
            }
        ]
    }
}

PlacerholderData 是一个存储三个主要属性和应从 actors 属性 中的嵌套 nodes 容器中检索的参与者列表的结构JSON 对象。

Placerholder 数据:

struct PlaceholderData: Codable {
    let filmCount: Int
    let year: Int
    let category: String
    let actors: [Actor]
}

Actor.swift:

struct Actor: Codable {
    let id: Int
    let name: String
}

我试图通过提供我自己的 init 来手动初始化解码器容器中的值来做到这一点。我怎样才能解决这个问题而不必有一个存储 nodes 对象的中间结构?

你可以使用 nestedContainer(keyedBy:)nestedUnkeyedContainer(forKey:) 像这样解码嵌套数组和字典来转换它进入你想要的结构。你在 init(decoder: ) 中的解码可能看起来像这样,

用于解码的 Actor 扩展,

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)
    }
}

用于解码的占位符数据扩展,

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
    }
}

然后,你可以这样解码,

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

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