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

标签 ios swift encoding decode codable

<分区>

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

在结构/类中我们可以使用泛型值,但如何在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. Raw value for enum case must be a literal

  2. Non-nominal type 'T' does not support explicit initialization

最佳答案

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

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>) 指定类型或使用继承来创建特定类型。

更新:

Generic 选项不起作用,因为 Codable 仍然需要知道 key ,因此您最终需要做大量的 key 检查并知道什么 key 与什么类型匹配,这使它变得毫无意义。

我确实设法解码了一个示例用户,它应该可重复用于城市和其他类型,但它似乎也有点冗长。

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")]

关于ios - 如何在 CodingKeys 枚举(可编码协议(protocol))中使用通用值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52553894/

相关文章:

encoding - 为什么 Go 在 base64 编码中使用位操作?

ios - 将泛型类作为参数传递给 swift 中的函数

ios - UIBarButtonItem 取消不起作用

ios - 用于查看的选项卡栏,关闭时返回到上一个选定的 View

java - 尝试将 UTF-8 文本写入文本文件时出现编译错误

c# - 使用带有 ISO-8859-1 编码的 XmlTextWriter 编写 XML 文件

ios - 当用户点击键盘时,UITextView 发布者不会发布信号

iphone - 在 XCode/XIB 中更改 "System"字体

xcode - 如何初始化相互依赖的属性

ios - AlamoFire 发送 Nil