【问题标题】:Using Codable to decode different classes using the same key使用 Codable 使用相同的密钥解码不同的类
【发布时间】:2019-02-19 20:56:16
【问题描述】:

我正在使用提供 2 个 JSON URL 的 API。每个 URL 都包含一个嵌套容器,该容器具有属于同一类和对象的不同属性。

JSON 网址 1

{
  "last_updated": 1535936629,
  "xyz": 5,
  "data": {
    "dataList": [
      {
        "id": "42",
        "a1": "a1value",
        "a2": "a2value",
      },
      // ,,,
    ]
  }
}

JSON 网址 2

{
  "last_updated": 1536639996,
  "xyz": 5,
  "data": {
    "dataList": [
      {
        "id": "42",
        "a3": "a3value",
        "a4": "a4value",
      },
      // ,,,
    ]
  }
}

我想使用这些 JSON URL 来使用嵌套的 dataList 列表中的项目创建单个 Codable CustomClass 对象,因此我创建了一个 Feed 结构来处理这 2 个 JSON 文件。

Feed.swift

import Foundation

Struct Feed: Decodable {
  var lastUpdated: Int
  var xyz: Int
  var data: KeyedDecodingContainer<Feed.dataCodingKey>
  var dataList: [CustomClass]

  enum CodingKeys: String, CodingKey {
    case lastUpdated = "last_updated"
    case xyz
    case data
  }

  enum dataCodingKey: String, CodingKey {
    case dataList
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.lastUpdated = try decoder.decode(Int.self, forKey: .lastUpdated)
    self.xyz = try container.decode(Int.self, forKey: .xyz)
    self.data = try container.nestedContainer(keyedBy: dataCodingKey.self, forKey: .data)
    self.dataList = try data.decode([CustomClass].self, forKey: .dataList)
  }
}

CustomClass.swift

class CustomClass: Decodable {

    var id: String
    var a1: String
    var a2: Double
    var a3: String
    var a4: String

    enum CodingKeys: String, CodingKey {
        case id
        case a1
        case a2
        case a3
        case a4
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try values.decode(String.self, forKey: .id)
        self.a1 = try values.decode(String.self, forKey: .a1)
        self.a2 = try values.decode(String.self, forKey: .a2)
        self.a3 = try values.decode(String.self, forKey: .a3)
        self.a4 = try values.decode(String.self, forKey: .a4)
    }
}

在我的 ViewController 中,我执行了两个单独的异步调用来获取数据:

ViewController.swift

var listOfCustomClass: [CustomClass]
var listOfCustomClass2: [CustomClass]

func getFeed(urlString: String, completionHandler: @escaping (_ result: Feed?) -> Void) {
    // parses JSON to return a Feed object

    guard let url = URL(string: urlString) else { return }
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        if error != nil {
            print(error!)
        }
        guard let data = data else { return }

        //Implement JSON decoding and parsing
         do {
            let feed = try JSONDecoder().decode(Feed.self, from: data)
            DispatchQueue.main.async {
                completionHandler(feed)
            }
        } catch {
            print(error)
        }
    }.resume()
}

getFeed(urlString: url1String) { result in
  // Obtain the contents of dataList from URL1
  if let feed = result {
    self.listOfCustomClass = feed.dataList
    self.getFeed(urlString: url2String) { result in
      //Upon completion, obtain the dataList info and populate the "a3" and "a4" attributes from CustomClass

      if let feed = result {
        let dataList2: [CustomClass] = feed.dataList

      // code to merge self.listOfCustomClass 1 and self.listOfCustomClass2 into a single [CustomClass] list with all attributes and store it as self.listOfCustomClass   

        // Upon completion, return the finalized station array for use
        DispatchQueue.main.async {
          completionHandler(self.listOfCustomClass)
        }
      }
    }
  }
}

我遇到的问题是dataList CodingKey 有不同的键 a1a2 如果来自 URL1 或 a3a4 如果来自 URL2。因此,只要 Codable init 方法在 dataList 容器中找不到 4 个键中的 2 个,它就会抱怨。

如何使用单个解码器实例化 a1、a2、a3 和 a4 创建一个 CustomClass 对象?

【问题讨论】:

  • 这里根本不清楚你在问什么。请阅读minimal reproducible example,然后阅读edit 您的问题,其中显示了演示问题的最小代码
  • 请给出一个实际不工作的代码示例。例如,如果您的类继承自 MKAnnotation,请在您的问题中表明这一点 - 但不要包含视图控制器中的所有代码 - 数据的下载方式不会影响数据的解码方式。
  • @AshleyMills 我已经编辑了我的问题以包含我在 cmets 中提供的增量信息,并在最后一句中澄清了问题。
  • 在收到答案后更改您的问题是不合适的,因为这会使您收到的答案无效。它甚至会使这些答案出错,并对回答者的声誉产生不利影响。如果您现在有新问题或其他问题,请创建一个新帖子并在那里提问;如果需要参考,您可以链接回此链接。我已经回滚了你的问题以对应给出的答案。
  • @AshleyMills 显然我没有在这里做事。我从您之前的回复中得知,目前尚不清楚问题是什么,并且向 cmets 添加增量信息是不好的做法,并且您要求我编辑问题主体本身。我为来回道歉。目前我不确定如何正确接收反馈。

标签: ios swift class codable


【解决方案1】:

我的建议是使用泛型。将dataList 对象的类型作为泛型类型传递给Feed。您甚至可以使用适当的dateDecodingStrategylastUpdated 解码为Date

struct Feed<T : Decodable>: Decodable {
    let lastUpdated: Date
    let xyz: Int
    let data: DataList<T>
}

struct DataList<T : Decodable> : Decodable {
    let dataList: [T]
}

dataList 对象的类型可以是任何符合Decodable 的对象,给定的 JSON 可以解码为这两个结构或类:

class CustomClass1 : Decodable {
    let id, a1, a2: String
}

class CustomClass2 : Decodable {
    let id, a3, a4: String
}

多种类型的好处是完全避免了任何键和类型检查。

例如解码第一个 JSON 写入

let json = """
{
    "last_updated": 1535936629,
    "xyz": 5,
    "data": {
        "dataList": [{"id": "42", "a1": "a1value", "a2": "a2value"}]
    }
}
"""

let data = Data(json.utf8)
do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    decoder.dateDecodingStrategy = .secondsSince1970
    let result = try decoder.decode(Feed<CustomClass1>.self, from: data)
    print(result)
} catch {
    print(error)
}

【讨论】:

  • 这应该是公认的答案
  • 你想如何理解每个应该解码哪种类型的类?
  • 类型在Feed&lt;CustomClass1&gt;中的泛型类型CustomClass1中指定。
【解决方案2】:

如果CustomClass 的 JSON 可能包含也可能不包含键 a1a2 等,那么它们必须是可选的……

let a1: String?
let a2: Double?
let a3: String?
let a4: String?

那只是使用的一个例子

a1 = try values.decodeIfPresent(String.self, forKey: .a1)

【讨论】:

  • 这个解决方案的问题是我打算让CustomClass继承MKAnnotation,这需要一个coordinate var的非可选初始化。
  • 在这种情况下,包含一个非可选的coordinate var - 在这种情况下,它必须存在于 JSON 中
  • 不幸的是,JSON 仅包含 latitude var 和 longitude var,而不包括 coordinate var。
  • 好的,所以你解码 latitudelongitude 并在你的 init 方法中创建一个 coordinate 对象。不过,这并未包含在您的原始问题中。如果没有所有信息,很难给出答案,尤其是在球门柱不断变化的情况下。
猜你喜欢
  • 2016-10-15
  • 1970-01-01
  • 2017-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多