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

时间:2018-03-22 19:40:05

标签: json swift codable decodable

鉴于以下JSON文档,我想创建一个具有四个属性的structfilmCount(Int),year(Int),category(String) ,和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是一个结构,存储三个主要属性和应该从JSON对象的nodes属性中的嵌套actors容器中检索的actor列表。

PlacerholderData:

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对象的中间结构的情况下解决此问题?

1 个答案:

答案 0 :(得分:3)

您可以使用 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)
    }
}

用于解码的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
    }
}

然后,您可以像这样解码它,

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

这里,基本思想是使用 nestedUntaedContainer(keyedBy:)和数组容器使用 nestedUnkeyedContainer(forKey :)

解码字典容器