【问题标题】:In Swift: How do I decode this JSON (which has variable keys)?在 Swift 中:如何解码这个 JSON(它有可变键)?
【发布时间】:2020-01-30 09:17:21
【问题描述】:

有一个 API 可以提供我想使用的 JSON 数据。我在下面给出了 JSON 的摘要。在顶层,每条记录的键是一个与记录本身的 ID 匹配的唯一 ID。这些键是引号中的整数(从 1 开始,未排序且可能不连续)。

读取 JSON 不是问题。 接收数据所需的可编码“响应”结构是什么?

if let response = try? JSONDecoder().decode(Response.self, from: data)

JSON

{
    "2546": {
        "id": "2546",
        "title": "Divis and the Black Mountain"
    },
    "1": {
      "id": "1",
      "title": "A la Ronde"
    },
    "2": {
      "id": "2",
      "title": "Aberconwy House"
    }
}

【问题讨论】:

  • 使用带有字符串键的Dictionary
  • 感谢以下回复。它们都非常有用,我希望我能“接受”所有这些(但我确实赞成它们)。我选择@Tharak 的答案只是因为他的代表得分最低。

标签: json swift


【解决方案1】:

我也遇到过这种情况,看来创建此端点的人并不真正了解 JSON 的工作原理... 试试这个,然后返回 response.values,这样你就有了一个项目列表

struct Item: Codable {
    let id, title: String
}
typealias Response = [String: Item]

【讨论】:

    【解决方案2】:

    使用更动态的CodingKey 版本。你可以在这里阅读更多信息:https://benscheirman.com/2017/06/swift-json/

    查看“动态编码键”部分

    【讨论】:

      【解决方案3】:

      Codable类型struct Response应该是,

      struct Response: Decodable {
          let id: String
          let title: String
      }
      

      现在,使用 [String:Response] 而不是像这样仅使用 Response 来解析 json data

      do {
          let response = try JSONDecoder().decode([String:Response].self, from: data)
          print(response) //["1": Response(id: "1", title: "A la Ronde"), "2546": Response(id: "2546", title: "Divis and the Black Mountain"), "2": Response(id: "2", title: "Aberconwy House")]
      } catch {
          print(error)
      }
      

      【讨论】:

        【解决方案4】:

        你应该实现一个自定义的CodingKey,类似这样:

        struct MyResponse {
            struct MyResponseItemKey: CodingKey {
                var stringValue: String
        
                init?(stringValue: String) {
                    self.stringValue = stringValue
                }
        
                var intValue: Int? { return nil }
        
                init?(intValue: Int) { return nil }
        
                static let id = MyResponseItemKey(stringValue: "id")!
                static let title = MyResponseItemKey(stringValue: "title")!
            }
        
            struct MyResponseItem {
                let id: String
                let subItem: MyResponseSubItem
            }
        
            struct MyResponseSubItem {
                let id: String
                let title: String
            }
        
            let responseItems: [MyResponseItem]
        }
        

        不确定每个项目的键和id的值是否总是相等,这就是MyResponse中有2个ID的原因。

        当然,MyResponse 应该符合Codable

        extension MyResponse: Codable {
            init(from decoder: Decoder) throws {
                let container = try decoder.container(keyedBy: MyResponseItemKey.self)
                responseItems = try container.allKeys.map { key in
                    let containerForKey = try container.nestedContainer(keyedBy: MyResponseItemKey.self, forKey: key)
                    let id = try containerForKey.decode(String.self, forKey: .id)
                    let title = try containerForKey.decode(String.self, forKey: .title)
                    return MyResponseItem(id: key.stringValue, subItem: MyResponseSubItem(id: id, title: title))
                }
            }
        
            func encode(to encoder: Encoder) throws {
                var container = encoder.container(keyedBy: MyResponseItemKey.self)
                for responseItem in responseItems {
                    if let key = MyResponseItemKey(stringValue: responseItem.id) {
                        var subItemContainer = container.nestedContainer(keyedBy: MyResponseItemKey.self, forKey: key)
                        try subItemContainer.encode(responseItem.subItem.id, forKey: .id)
                        try subItemContainer.encode(responseItem.subItem.title, forKey: .title)
                    }
                }
            }
        }
        

        这就是你可以使用MyResponse的方法:

        let jsonString = """
        {
            "2546": {
                "id": "2546",
                "title": "Divis and the Black Mountain"
            },
            "1": {
              "id": "1",
              "title": "A la Ronde"
            },
            "2": {
              "id": "2",
              "title": "Aberconwy House"
            }
        }
        """
        if let dataForJSON = jsonString.data(using: .utf8),
            let jsonDecoded = try? JSONDecoder().decode(MyResponse.self, from: dataForJSON) {
            print(jsonDecoded.responseItems.first ?? "")
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted
            if let dataFromJSON = try? encoder.encode(jsonDecoded) {
                let jsonEncoded = String(data: dataFromJSON, encoding: .utf8)
                print(jsonEncoded ?? "")
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2020-12-19
          • 1970-01-01
          • 1970-01-01
          • 2018-08-15
          • 1970-01-01
          • 2021-10-24
          • 1970-01-01
          • 1970-01-01
          • 2019-03-24
          相关资源
          最近更新 更多