【问题标题】:Swift 4 Decodable: struct from nested arraySwift 4 Decodable:来自嵌套数组的结构
【发布时间】:2018-09-01 09:22:41
【问题描述】:

鉴于以下 JSON 文档,我想创建一个具有四个属性的 structfilmCount (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"
                }
            }
        ]
    }
}

PlacerholderData 是一个结构体,用于存储三个主要属性和应该从 JSON 对象的 actors 属性内的嵌套 nodes 容器中检索的演员列表。

占位符数据:

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 对象的中间结构?

【问题讨论】:

  • 您需要另一个结构 struct Actors: Codable { let nodes: [Actor] } 并在 PlaceholderData 结构中使用它而不是 [Actor]let actors: Actors

标签: json swift codable decodable


【解决方案1】:

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

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

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

的数组容器

【讨论】:

  • 您好,感谢您的回答,尽管我尝试使用它并收到下一个错误:
猜你喜欢
  • 2021-08-07
  • 2017-11-16
  • 1970-01-01
  • 1970-01-01
  • 2018-07-08
  • 2017-11-26
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
相关资源
最近更新 更多