首页 > 解决方案 > 如何扁平化嵌套的 JSON 结构

问题描述

嵌套的 JSON

{
    "items": [{
        "track": {
            "name": "Never Gonna Give You Up"
        }
    }]
}

我希望它看起来像这样

{
    "tracks": [{
        "name": "Never Gonna Give You Up"
    }]
}

我试过的

  struct TrackResponse: Decodable {
    let tracks: [Track]

    enum CodingKeys: String, CodingKey {
      case tracks = "items"
    }

    enum TrackKeys: String, CodingKey {
      case track
    }

    init(from decoder: Decoder) throws {
      let outerContainer = try decoder.container(keyedBy: CodingKeys.self)
      let trackContainer = try outerContainer.nestedContainer(keyedBy: TrackKeys.self,
                                                              forKey: .tracks)

      self.tracks = try trackContainer.decode([Track].self, forKey: .track)
    }

    struct Track: Decodable {
      var name: String
    }
  }

我收到此错误

预期解码 Dictionary<String, Any> 但找到了一个数组。

更新

我创建了一个具有更多上下文和一些更改的新问题,并通过遵循@vadian 的回复最终达到了我的目标。

检查一下,看看它是否也能帮助你:将 具有细微差异的 JSON 结构解码为一种格式

标签: jsonswiftdecoding

解决方案


嵌套容器为您的代码增加了一定程度的复杂性,您可以通过使用普通的旧可解码器来避免这种情况。

因此,只需声明一个(私有)结构Item,在 的初始化程序中对其进行解码TrackResponse,然后从那里提取轨道:

struct TrackResponse: Decodable {
    var tracks: [Track]
    
    enum CodingKeys: String, CodingKey {
        case items
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let items = try container.decode([Item].self, forKey: .items)
        tracks = items.map(\.track)
    }
    
    struct Track: Decodable {
        var name: String
    }
    
    private struct Item: Decodable {
        var track: Track
    }
}

我的观点是,您应该尝试尽可能多地模仿 JSON 结构,这将更容易应对 JSON 结构的未来变化。如果需要用另一种形式表示数据,在解码成功后进行后处理。

我自己,我会走得更远,简化整个响应代码:

struct TrackResponse: Decodable {
    var items: [Item]
    
    struct Track: Decodable {
        var name: String
    }
    
    struct Item: Decodable {
        var track: Track
    }
}

,然后在收到响应后简单地提取曲目:

let tracks = response.items.map(\.track)

您编写的代码越少,您引入错误的机会就越小;)


推荐阅读