根据 ClassType 解码字典

问题描述 投票:0回答:1

假设我有一个变量

clients: [String: Client]
。 Client 是我的父类,我有一个子类
SpecialClient
。这两个类都是可编码的,我想将 JSON 字符串解码到字典中。在这本词典中,我希望同时拥有两种类型的客户。

为此,JSON 字符串中的每个客户端都有一个变量

clientType
,它定义它是否是
SpecialClient
。我现在需要读取这个值,并根据它在字典中添加
SpecialClient
或普通
Client

目前,我只有这样的东西,显然只是在字典中生成 Clients 。我做了一些研究,但找不到方法。

class Object: Codable {
    var clients: [String:Client]


    init(from decoder: any Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.clients = try container.decode([String : Client].self, forKey: .clients)
    }
}

拥有一个包含来自 Client 和 SpecialClient 类的客户端的容器。

更多上下文:JSON 内容看起来像这样:

["clients": {
"clientId1111" =     {
    clientType = BASIC_TYPE;
    label = "Test";
    ...
};]
json swift decoding
1个回答
0
投票

由于有关 JSON 结构的问题缺乏清晰度,因此在此答案中做出了一个假设。我假设结构是:

{
  "clients": [
    {
      "clientId1111": {
        "clientType": "BASIC_TYPE",
        "label": "Test1"
      }
    },
    //...
    {
      "clientId1114": {
        "clientType": "SPECIAL_TYPE",
        "label": "Test4",
        "specialCode": 4
      }
    }
  ]
}

即一个数组字典,在顶层由

clients
键控,如果不是这种情况,您可能需要调整答案。

在此基础上我创建了一些模型:

基本

Client
模型。这符合开箱即用的可解码,因此不需要自定义解码。

class Client: Decodable {
   let label: String
   let clientType: String
   
   init(label: String, clientType: String) {
      self.label = label
      self.clientType = clientType
   }
}

SpecialClient
型号:

class SpecialClient: Client {
   let specialCode: Int //just to differentiate from Client
   enum CodingKeys: CodingKey {
      case label, clientType, specialCode
   }
   
   required init(from decoder: any Decoder) throws {
      let container = try decoder.container(keyedBy: CodingKeys.self)
      specialCode = try container.decode(Int.self, forKey: .specialCode)
      let label = try container.decode(String.self, forKey: .label)
      let type = try container.decode(String.self, forKey: .clientType)
      super.init(label: label, clientType: type)
   }
}

使用自定义解码器并使用数据字段调用

super.init
。 [我觉得有更好的方法来处理这个问题,但自从我使用 coddle 继承以来已经很久了......!]

然后大部分解码发生在

Object
模型中


class Object: Decodable {
   var clients: [String:Client]
   
   enum CodingKeys: CodingKey {
      case clients
   }
   
   
   required init(from decoder: any Decoder) throws {
      var imports = [ [String: Client] ]()
      do {
         let container = try decoder.container(keyedBy: CodingKeys.self)
         var dataContainer = try container.nestedUnkeyedContainer(forKey: .clients)
         while !dataContainer.isAtEnd {
            do {
               let imported = try dataContainer.decode([String: SpecialClient].self)
               imports.append(imported)
            } catch {
               let imported = try dataContainer.decode([String: Client].self)
               imports.append(imported)
            }
         }
      } catch {
         fatalError(error.localizedDescription)
      }
      clients = imports.reduce(into:[String: Client]()){ master, childDict in  
         childDict.forEach{  childPair in
            master[childPair.key] = childPair.value
         }
      }
   }
}

这里它使用嵌套的无键容器,因此您可以访问字典键(

clientId###
)及其值(
Client
SpecialClient
)。
该方法迭代未设置密钥的容器中的每个条目,并且对于每个条目最初尝试解码更专门的子类,然后如果失败,则尝试解码 catch 块中的更简单的父类。解码后的值对,即
[String: Client/SpecialClient]
字典,被附加到临时数组中。

最后,它将临时字典数组减少(扁平化)到主

clients
字典中。

© www.soinside.com 2019 - 2024. All rights reserved.