json - Swift 条件符合两种情况

标签 json swift generics protocols decodable

简短的问题是,当满足其中一个条件时,如何使类型有条件地符合具有两个条件的协议(protocol)?

我有一个通用类型 NetworkResponse<Data> .它代表服务器响应。这是它的定义方式:

enum NetworkResponse<Data> {
    case success(Data)
    case error(ServerError)
}

我想制作NetworkResponse符合 Decodable .这是我的服务器响应格式:

{
    "data": {
        "someKey": "someValue",
        "anotherKey": 15
    },
    "meta": {
        "returnCode": 0,
        "returnMessage": "operation is successful"
    }
}

data部分取决于提出的要求。 meta部分代表一些关于响应的元数据。比如是否成功,如果不成功,错误是什么。

这就是我实现 Decodable 的方式:

extension NetworkResponse: Decodable where Data: Decodable {
    enum CodingKeys: CodingKey {
        case meta
        case data
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let metaValue = try container.decode(ServerError.self, forKey: .meta)

        if metaValue.code != 0 {
            self = .error(metaValue)
        } else {
            self = .success(try container.decode(Data.self, forKey: .data))
        }
    }
}

到目前为止一切顺利。但这是我的问题。对于一些不需要返回任何数据的 api,data省略了响应部分。在这种情况下,我的回复将如下所示:

{
    "meta": {
        "returnCode": 0,
        "returnMessage": "operation is successful"
    }
}

在这种情况下,我想将响应 json 解码为 NetworkResponse<Void> .但是因为 Void不符合Decodable (因为它是非标称类型)编译器给出错误。

为了克服这个问题,我尝试为 Decodable 创建更专业的扩展其中 DataVoid像这样:

extension NetworkResponse: Decodable where Data == Void {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let metaValue = try container.decode(AppErrors.Server.self, forKey: CodingKeys.meta)

        if metaValue.code != 0 {
            self = .error(metaValue)
        } else {
            self = .success(())
        }
    }
}

但编译器仍然像这样编译:Conflicting conformance of 'NetworkResponse<Data>' to protocol 'Decodable'; there cannot be more than one conformance, even with different conditional bounds .

那么我怎样才能创建单独的 init(from:) Data 时使用的函数是Void

最佳答案

我强烈建议您将泛型参数的类型名称从 Data 改掉,因为它很容易与 Swift 中广泛使用的 Foundation.Data 混淆。

关于问题本身,您可以创建一个空结构来表示“void”并向您的 NetworkResponse 添加一个新案例:

struct EmptyData: Decodable {}

enum NetworkResponse<T> {
    case success(T)
    case successWithEmptyData
    case error(ServerError)
}

extension NetworkResponse: Decodable where T: Decodable {
    private enum CodingKeys: CodingKey {
        case meta
        case data
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let metaValue = try container.decode(ServerError.self, forKey: .meta)

        if metaValue.code != 0 {
            self = .error(metaValue)
        } else if T.self == EmptyData.self {
            self = .successWithEmptyData
        } else {
            self = .success(try container.decode(T.self, forKey: .data))
        }
    }
}

let response = try! JSONDecoder().decode(NetworkResponse<EmptyData>.self, from: jsonData)

或者,您可以让您的 .success 案例包含一个 T? 当您不希望返回任何数据并相应地解码时。

关于json - Swift 条件符合两种情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51449163/

相关文章:

php - 使用 mysql 和 PHP 创建嵌套 JSON

c# - Json.NET 和 NHibernate - 反序列化持久对象

带有内容类型 : text/html with injected html tags 的 java REST 保证响应正文

javascript - 有没有办法将包含日期的字符串数组从服务器端传递到客户端?

ios - Bundle.main.path(forResource :ofType:inDirectory:) returns nil when directory and filename are correct

ios - SwiftyJSON 不断返回空对象

c# - 将一个对象同时转换为两个接口(interface),调用泛型方法

java - 如何创建其条目具有通用字段的项目数组?

ios - 如何在 IQKeyboardManager iOS Swift 3 中隐藏工具栏

generics - 对泛型函数的引用