我正在与一个喜欢将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
}
通过这种方式,我可以将目标可解码类以及封装该对象的密钥传递进来。
答案 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
}