[英]Value of protocol type 'Encodable' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols
I have the following Swift code我有以下 Swift 代码
func doStuff<T: Encodable>(payload: [String: T]) {
let jsonData = try! JSONEncoder().encode(payload)
// Write to file
}
var things: [String: Encodable] = [
"Hello": "World!",
"answer": 42,
]
doStuff(payload: things)
results in the error导致错误
Value of protocol type 'Encodable' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols
How to fix?怎么修? I guess I need to change the type of things
, but I don't know what to.我想我需要改变things
的类型,但我不知道该怎么做。
Additional info:附加信息:
If I change doStuff
to not be generic, I simply get the same problem in that function如果我将doStuff
更改为不是通用的,我只会在 function 中遇到同样的问题
func doStuff(payload: [String: Encodable]) {
let jsonData = try! JSONEncoder().encode(payload) // Problem is now here
// Write to file
}
Encodable
cannot be used as an annotated type. Encodable
不能用作带注释的类型。 It can be only used as a generic constraint.它只能用作通用约束。 And JSONEncoder
can encode only concrete types.而JSONEncoder
只能编码具体类型。
The function function
func doStuff<T: Encodable>(payload: [String: T]) {
is correct but you cannot call the function with [String: Encodable]
because a protocol cannot conform to itself.是正确的,但是您不能使用[String: Encodable]
调用 function,因为协议不能符合自身。 That's exactly what the error message says.这正是错误消息所说的。
The main problem is that the real type of things
is [String:Any]
and Any
cannot be encoded.主要问题是things
的真实类型是[String:Any]
并且Any
无法编码。
You have to serialize things
with JSONSerialization
or create a helper struct.您必须使用JSONSerialization
序列化things
或创建辅助结构。
You're trying to conform T
to Encodable
which is not possible if T == Encodable
.您正在尝试使T
符合Encodable
,如果T == Encodable
,这是不可能的。 A protocol does not conform to itself.协议不符合自身。
Instead you can try:相反,您可以尝试:
func doStuff<T: Hashable>(with items: [T: Encodable]) {
...
}
What you are trying to do is possible with protocol extensions:您可以通过协议扩展来尝试做的事情:
protocol JsonEncoding where Self: Encodable { }
extension JsonEncoding {
func encode(using encoder: JSONEncoder) throws -> Data {
try encoder.encode(self)
}
}
extension Dictionary where Value == JsonEncoding {
func encode(using encoder: JSONEncoder) throws -> [Key: String] {
try compactMapValues {
try String(data: $0.encode(using: encoder), encoding: .utf8)
}
}
}
Every type that could be in your Dictionary
will need to conform to our JsonEncoding
protocol. Dictionary
中可能包含的每种类型都需要符合我们的JsonEncoding
协议。 You used a String
and an Int
, so here are extensions that add conformance for those two types:您使用了String
和Int
,因此这里有为这两种类型添加一致性的扩展:
extension String: JsonEncoding { }
extension Int: JsonEncoding { }
And here is your code doing what you wanted it to do:这是你的代码做你想做的事:
func doStuff(payload: [String: JsonEncoding]) {
let encoder = JSONEncoder()
do {
let encodedValues = try payload.encode(using: encoder)
let jsonData = try encoder.encode(encodedValues)
// Write to file
} catch {
print(error)
}
}
var things: [String: JsonEncoding] = [
"Hello": "World!",
"answer": 42
]
doStuff(payload: things)
You didn't ask about decoding, so I didn't address it here.你没有问解码,所以我没有在这里解决。 You will either have to have knowledge of what type is associated with what key, or you will have to create an order in which you try to decode values (Should a 1
be turned into an Int
or a Double
...).您要么必须知道什么类型与什么键相关联,要么必须创建一个尝试解码值的顺序(应该将1
转换为Int
或Double
...)。
I answered your question without questioning your motives, but there is likely a better, more "Swifty" solution for what you are trying to do...我在没有质疑您的动机的情况下回答了您的问题,但是对于您正在尝试做的事情,可能有一个更好、更“迅速”的解决方案......
You can use the where
keyword combined with Value
type, like this:您可以将where
关键字与Value
类型结合使用,如下所示:
func doStuff<Value>(payload: Value) where Value : Encodable {
...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.