编码通用结构数组
Encoding an array of generic structs
我正在尝试进行一个 API 调用,它需要一个 JSON 请求正文,如下所示:
[
{ "op": "replace", "path": "/info/name", "value": "TestName" },
{ "op": "replace", "path": "/info/number", "value": 100 },
{ "op": "replace", "path": "/info/location", "value": ["STATE", "CITY"] },
{ "op": "replace", "path": "/privacy/showLocation", "value": true }
]
我有一些 op
和 path
值的枚举:
enum ChangeOp: String, Encodable {
case replace
case append
}
enum ChangePath: String, Encodable {
case name = "/info/name"
case number = "/info/number"
case location = "/info/location"
case showLocation = "/privacy/showLocation"
}
在 中,我发现你必须使用一个协议来启用通用结构数组的创建,所以我有以下协议和结构:
protocol UserChangeProto {
var op: ChangeOp { get }
var path: ChangePath { get }
}
struct UserChange<ValueType: Encodable>: Encodable, UserChangeProto {
let op: ChangeOp
let path: ChangePath
let value: ValueType
}
这里是编码发生的地方:
func encodeChanges(arr: [UserChangeProto]) -> String? {
let encoder = JSONEncoder()
guard let jsonData = try? encoder.encode(arr) else {
return nil
}
return String(data: jsonData, encoding: String.Encoding.utf8)
}
func requestUserChanges(changes: String) {
print(changes)
// make API request ...
}
requestUserChanges(changes:
encodeChanges(arr: [
UserChange(op: .replace, path: .name, value: "TestName"),
UserChange(op: .replace, path: .number, value: 100),
UserChange(op: .replace, path: .location, value: ["STATE", "CITY"]),
UserChange(op: .replace, path: .showLocation, value: true)
]) ?? "null"
)
问题是,当我尝试 运行 encoder.encode(arr)
时,出现以下错误:Value of protocol type 'UserChangeProto' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols
.
我的问题是,我该如何解决这个错误?或者换句话说,编码通用结构数组的最简单方法是什么?
编辑:看起来这是 Swift 语言本身的问题,Swift team is looking into。我不确定如何在这里进行...
您可能会发现类型擦除的编码器很有用:https://github.com/Flight-School/AnyCodable
使用上面的 AnyEncodable:
struct Change<V: Encodable>: Encodable {
enum Op: String, Encodable {
case replace
case append
}
enum Path: String, Encodable {
case name = "/info/name"
case number = "/info/number"
case location = "/info/location"
case showLocation = "/privacy/showLocation"
}
var op: Op
var path: Path
var value: V
}
let encoder = JSONEncoder()
let changes: [Change<AnyEncodable>] = [
Change(op: .append, path: .name, value: "Foo"),
Change(op: .replace, path: .number, value: 42)
]
let r = try? encoder.encode(changes)
String(data: r!, encoding: .utf8)
如您所愿
我正在尝试进行一个 API 调用,它需要一个 JSON 请求正文,如下所示:
[
{ "op": "replace", "path": "/info/name", "value": "TestName" },
{ "op": "replace", "path": "/info/number", "value": 100 },
{ "op": "replace", "path": "/info/location", "value": ["STATE", "CITY"] },
{ "op": "replace", "path": "/privacy/showLocation", "value": true }
]
我有一些 op
和 path
值的枚举:
enum ChangeOp: String, Encodable {
case replace
case append
}
enum ChangePath: String, Encodable {
case name = "/info/name"
case number = "/info/number"
case location = "/info/location"
case showLocation = "/privacy/showLocation"
}
在
protocol UserChangeProto {
var op: ChangeOp { get }
var path: ChangePath { get }
}
struct UserChange<ValueType: Encodable>: Encodable, UserChangeProto {
let op: ChangeOp
let path: ChangePath
let value: ValueType
}
这里是编码发生的地方:
func encodeChanges(arr: [UserChangeProto]) -> String? {
let encoder = JSONEncoder()
guard let jsonData = try? encoder.encode(arr) else {
return nil
}
return String(data: jsonData, encoding: String.Encoding.utf8)
}
func requestUserChanges(changes: String) {
print(changes)
// make API request ...
}
requestUserChanges(changes:
encodeChanges(arr: [
UserChange(op: .replace, path: .name, value: "TestName"),
UserChange(op: .replace, path: .number, value: 100),
UserChange(op: .replace, path: .location, value: ["STATE", "CITY"]),
UserChange(op: .replace, path: .showLocation, value: true)
]) ?? "null"
)
问题是,当我尝试 运行 encoder.encode(arr)
时,出现以下错误:Value of protocol type 'UserChangeProto' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols
.
我的问题是,我该如何解决这个错误?或者换句话说,编码通用结构数组的最简单方法是什么?
编辑:看起来这是 Swift 语言本身的问题,Swift team is looking into。我不确定如何在这里进行...
您可能会发现类型擦除的编码器很有用:https://github.com/Flight-School/AnyCodable
使用上面的 AnyEncodable:
struct Change<V: Encodable>: Encodable {
enum Op: String, Encodable {
case replace
case append
}
enum Path: String, Encodable {
case name = "/info/name"
case number = "/info/number"
case location = "/info/location"
case showLocation = "/privacy/showLocation"
}
var op: Op
var path: Path
var value: V
}
let encoder = JSONEncoder()
let changes: [Change<AnyEncodable>] = [
Change(op: .append, path: .name, value: "Foo"),
Change(op: .replace, path: .number, value: 42)
]
let r = try? encoder.encode(changes)
String(data: r!, encoding: .utf8)
如您所愿