如何在CodingKeys枚举(可编码协议)中使用通用值

时间:2018-09-28 10:59:48

标签: ios swift encoding decode codable

编译器抱怨:“枚举大小写的原始值必须是文字”

在struct / class中,我们可以使用泛型值,但是如何在enum中使用泛型。

以下是我要建立一般结构的服务器响应

{
  "status": true,
  "message": "message",
  "error" : "error if any"
  "any key" : "Any kind of data"
}

在上面的示例中,“任何键”部分都很棘手。对于不同的服务呼叫,“任何键”都将不同。

用于用户城市列表:

{
  "status": true,
  "message": "message",
  "error" : ""
  "cities" : "city list"
}

用于用户状态列表:

{
  "status": true,
  "message": "message",
  "error" : ""
  "states" : "state list"
}

用于用户帖子:

{
  "status": true,
  "message": "message",
  "error" : ""
  "posts" : "list of posts"
}

您可以看到每个服务调用具有相同的“状态”,“消息”和“错误”键,并且 数据具有不同的键“城市”,“状态”,“帖子”等

所以我想创建一个通用的结构,将所有这些都包括在内。

我按照以下方式进行,但卡在了不同的键上。

struct Response<T>: Codable  {


let message : String? // common in every service call
let status : Bool? // common in every service call
let errors: String? // common in every service call
let data : T? // it will be different for every call

enum CodingKeys: String, CodingKey {

    case message = "message"
    case status = "status"
    case data = <key>    //Here what to use?
    case errors = "errors"
}

init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)

    message = try values.decodeIfPresent(String.self, forKey: .message)
    status = try values.decodeIfPresent(Bool.self, forKey: .status)
    errors = try values.decodeIfPresent(String.self, forKey: .errors)
    data = try T(from: decoder)
}

}

我可能要做什么?

如果是,该如何实现?

任何帮助将不胜感激!

尝试时出现以下错误。

  
      
  1. 枚举大小写的原始值必须为文字

  2.   
  3. 非标称类型“ T”不支持显式初始化

  4.   

2 个答案:

答案 0 :(得分:2)

一个选项是(实际上不起作用,请参见下面的评论):

struct Response<T>: Codable  {
    let message : String? // if these exist in every call do they have to be optional?
    let status : Bool? 
    let errors: String?
    let data: T
}

struct Users: Codable {
    let users: [User]
}

struct Cities: Codable {
    let cities: [City]
}

let response = JSONDecoder().decode(Response<Users>.self, from: data)

差不多,但是没有泛型:

class Response: Codable  {
    let message : String? // if these exist in every call do they have to be optional?
    let status : Bool? 
    let errors: String?
}

class UserResponse: Response, Codable {
    let users: [User]
}

class CitiesResponse: Response, Codable {
    let cities: [City]
}

您的主要问题是Codable需要知道要解码和映射到的类型,因此您需要使用泛型(Response<Users>)指定类型,或者使用继承来创建特定类型。

更新:

通用选项不起作用,因为Codable仍然需要知道密钥,因此最终需要进行大量的密钥检查,并知道哪些密钥与哪种类型匹配,从而使其毫无意义。

我确实设法对一个示例用户进行了解码,该示例用户应该可以在城市和其他类型中重复使用,但这似乎有些麻烦。

import PlaygroundSupport

let userJsonData = """
{
"status": true,
"message": "message",
"error" : "error if any",
"users" : [{
    "id": 1,
    "name": "John"
}, {
    "id": 2,
    "name": "Steve"
}]
}
""".data(using: .utf8)!

class Response: Codable {
    let error: String
    let status: Bool
    let message: String
}

struct User: Codable {
    let id: Int
    let name: String
}

class Users: Response {
    var users: [User]

    enum CodingKeys: String, CodingKey {
        case users
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        users = try values.decode([User].self, forKey: .users)
        try super.init(from: decoder)
    }
}

do {
     let response = try JSONDecoder().decode(Users.self, from: userJsonData)
    print(response.users)
} catch {
    print(error)
}

输出:

  

[__ lldb_expr_22.User(id:1,name:“ John”),__lldb_expr_22.User(id:2,name:“ Steve”)]

答案 1 :(得分:0)

更新

由于log需要文字:

console.log(JSON.stringify(object, null, "  "));

编码键只是enum类认为可接受的字符串或整数的集合。

protocol ResponseData: Decodable {
    static var attributeName: String { get }
}

struct Response<T>: Decodable  where T: ResponseData {
    let message : String
    let status : Bool
    let errors: String
    let data: T

由于它们是结构而不是枚举,因此必须手动创建它们。

CodingKey

这是必需的,因为即使 struct MyCodingKeys: CodingKey { var stringValue: String var intValue: Int? init?(intValue: Int) { return nil } init?(stringValue: String) { if let _ = DefaultCodingKeys.init(rawValue: stringValue){ // OK } else if stringValue == T.attributeName { // OK } else { // NOT OK return nil } self.stringValue = stringValue self.intValue = nil } enum DefaultCodingKeys: String, CodingKey { case message = "message" case status = "status" case errors = "errors" } 被命名为 static var data: MyCodingKeys { return MyCodingKeys(stringValue: T.attributeName)! } static var message: MyCodingKeys { return MyCodingKeys(stringValue: "message")! } static var errors: MyCodingKeys { return MyCodingKeys(stringValue: "errors")! } static var status: MyCodingKeys { return MyCodingKeys(stringValue: "status")! } } MyCodingKeys也不会使用它,因为CodingKeys为此需要Swift

Swift