使用Codable使用同一密钥解码不同的类 [英] Using Codable to decode different classes using the same key

查看:89
本文介绍了使用Codable使用同一密钥解码不同的类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm working with an API that provides 2 JSON URLS. Each URL contains a nested container with different attributes that belong to the same class and object.

JSON URL 1

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

JSON URL 2

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

我想使用这些JSON URL来使用嵌套的 dataList 列表,所以我创建了一个 Feed 结构来处理这2个JSON文件。

I want to use these JSON URLS to create a single Codable CustomClass object using the items in the nested dataList list, so I created a Feed struct to handle these 2 JSON files.

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中,我执行了两个单独的异步调用来获取数据:

In my ViewController, I do two separate asynchronous calls to obtain the data:

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具有不同的键 a1 a2 (如果来自URL1或 a3 a4 (如果来自URL2)。因此,只要在 dataList 容器中找不到4个键中的2个键,Codable init方法就会抱怨。

The problem I'm running into is that the dataList CodingKey has different keys a1 or a2 if coming from URL1 or a3, a4 if coming from URL2. Therefore the Codable init method is complaining whenever it can't find 2 of 4 keys in the dataList container.

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

How can I approach creating one CustomClass object with a1, a2, a3, and a4 instantiated using a single Decoder?

推荐答案

如果JSON到 CustomClass 可能包含或可能包含或不包含键 a1 a2 等,那么它们必须是可选的……

If the JSON to CustomClass may or may or may not contain keys a1, a2, etc, then they must be optional…

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

然后只是使用情况

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

这篇关于使用Codable使用同一密钥解码不同的类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆