Swift:模板定义中的常量

时间:2019-05-20 17:48:36

标签: ios swift decodable jsondecoder

我正在与一个喜欢将json主体封装在另一个对象(例如数据)中的后端开发人员一起工作:

示例:

  

获取:/ user / current:

{
  data: {
          firstName: "Evan",
          lastName: "Stoddard"
        }
}

我只是想在响应上调用json解码,以获得我创建的User结构,但是添加的数据对象需要另一个结构。为了解决这个问题,我创建了一个通用模板类:

struct DecodableData<DecodableType:Decodable>:Decodable {

    var data:DecodableType

}

现在,我可以获取我的json有效负载,并且如果要获取User结构,只需获取模板的data属性:

let user = JSONDecoder().decode(DecodableData<User>.self, from: jsonData).data

这很好而且很花哨,直到有时data键并不总是data

我觉得这很可能是琐碎的事情,但是有什么办法可以在模板定义中添加参数,以便可以更改枚举编码键,因为数据键可能会更改?

类似以下内容?

struct DecodableData<DecodableType:Decodable, Key:String>:Decodable {

    enum CodingKeys: String, CodingKey {
        case data = Key
    }

    var data:DecodableType

}

通过这种方式,我可以将目标可解码类以及封装该对象的密钥传递进来。

2 个答案:

答案 0 :(得分:2)

无需编码键。取而代之的是,您需要一个简单的容器,将JSON解析为一个字典,该字典恰好具有一对键值对,并丢弃键。

struct Container<T>: Decodable where T: Decodable {
    let value: T

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let dict = try container.decode([String: T].self)

        guard dict.count == 1 else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "expected exactly 1 key value pair, got \(dict.count)")
        }

        value = dict.first!.value
    }
}

如果JSON为空或具有多个键值对,则会引发异常。

假设一个简单的结构,例如

struct Foo: Decodable, Equatable {
    let a: Int
}

您可以解析它而不必考虑密钥:

let foo1 = try! JSONDecoder().decode(
    Container<Foo>.self,
    from: #"{ "data": { "a": 1 } }"#.data(using: .utf8)!
).value

let foo2 = try! JSONDecoder().decode(
    Container<Foo>.self,
    from: #"{ "doesn't matter at all": { "a": 1 } }"#.data(using: .utf8)!
).value

foo1 == foo2 // true

这也适用于以null作为值的JSON响应,在这种情况下,您需要将其解析为您类型的可选内容:

let foo = try! JSONDecoder().decode(
    Container<Foo?>.self,
    from: #"{ "data": null }"#.data(using: .utf8)!
).value // nil

答案 1 :(得分:0)

尝试如下所示:

struct GenericCodingKey: CodingKey {
    var stringValue: String

    init(value: String) {
        self.stringValue = value
    }

    init?(stringValue: String) {
        self.stringValue = stringValue
    }

    var intValue: Int?

    init?(intValue: Int) {
        return nil
    }
}

struct DecodableData<DecodableType: CustomDecodable>: Decodable {

    var data: DecodableType

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: GenericCodingKey.self)
        data = try container.decode(DecodableType.self, forKey: GenericCodingKey(value: DecodableType.dataKey))
    }
}

protocol CustomDecodable: Decodable {
    static var dataKey: String { get }
}

extension CustomDecodable {
    static var dataKey: String {
        return "data" // This is your default
    }
}


struct CustomDataKeyStruct: CustomDecodable {
    static var dataKey: String = "different"
}

struct NormalDataKeyStruct: CustomDecodable {
    //Change Nothing
}