Swift: 模板定义中的常量
Swift: Constant in Template Definition
我正在与喜欢将 json 主体封装在另一个对象(例如数据)中的后端开发人员合作:
示例:
GET: /user/current:
{
data: {
firstName: "Evan",
lastName: "Stoddard"
}
}
我只想对响应调用 json 解码以获取我创建的用户结构,但添加的数据对象需要另一个结构。为了解决这个问题,我创建了一个通用模板 class:
struct DecodableData<DecodableType:Decodable>:Decodable {
var data:DecodableType
}
现在我可以获取我的 json 负载,如果我想获取用户结构,只需获取我的模板的数据 属性:
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
}
这样我就可以传入目标可解码 class 以及封装该对象的密钥。
试试这样的东西:
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
}
无需编码键。相反,您需要一个简单的容器来将 JSON 解析为具有一对 key-value 的字典,并丢弃密钥。
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 为空或有多个 key-value 对,则会引发异常。
假设一个简单的结构,例如
struct Foo: Decodable, Equatable {
let a: Int
}
不管key如何都可以解析:
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
我正在与喜欢将 json 主体封装在另一个对象(例如数据)中的后端开发人员合作:
示例:
GET: /user/current:
{
data: {
firstName: "Evan",
lastName: "Stoddard"
}
}
我只想对响应调用 json 解码以获取我创建的用户结构,但添加的数据对象需要另一个结构。为了解决这个问题,我创建了一个通用模板 class:
struct DecodableData<DecodableType:Decodable>:Decodable {
var data:DecodableType
}
现在我可以获取我的 json 负载,如果我想获取用户结构,只需获取我的模板的数据 属性:
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
}
这样我就可以传入目标可解码 class 以及封装该对象的密钥。
试试这样的东西:
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
}
无需编码键。相反,您需要一个简单的容器来将 JSON 解析为具有一对 key-value 的字典,并丢弃密钥。
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 为空或有多个 key-value 对,则会引发异常。
假设一个简单的结构,例如
struct Foo: Decodable, Equatable {
let a: Int
}
不管key如何都可以解析:
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