How to encode Dictionary with JSONEncoder in Swift 4
我想用 JSONEncoder 将 Dictionary 编码为 json。
它看起来像一个请求,接收一个字典作为参数并将其编码为 json 作为 http 正文。
代码如下所示:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
let dict = [“name”:”abcde”]
protocol Request { extension Request { var body: Data? { struct BaseRequest: Request { let req = BaseRequest(params: dict) |
但是这段代码出现错误
Fatal error: Dictionary does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.
我怎样才能使它可编码?
- 为什么不使用 JSONSerializer 呢?你想防止 Any 依赖吗?
- 是的,我需要参数为 [String: Any]
- 哪些类型最终可以作为字典中的值?这就像不是真正的 Any 而是几种已知类型之一,对吧?通常,最好的解决方案是使用这些类型作为关联值进行枚举,以确认 Encodable。
- 最后我在 Request 中添加了 associatetype
你必须按如下方式引入类型擦除:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
struct AnyEncodable: Encodable {
let value: Encodable func encode(to encoder: Encoder) throws { } struct Model: Encodable { var params: [String: AnyEncodable] } let encoder = JSONEncoder() |
- 不幸的是,像这样的类型擦除会阻止编码器在编码之前拦截类型。这意味着如果您尝试对可能具有编码策略的类型(例如 Dates 或 Data)进行编码,它将不会被应用。
- @ItaiFerber AnyEncodable 装饰它收到的 Encodable 的值。它使用 Encodable 本身定义的实现(每个具体实现的定义不同)。我看不出它是如何破坏不同的编码策略的,除非您尝试输入强制类型转换,这是一个非常糟糕的主意。
- 此外,如果您需要打破封装,您可以键入 AnyEncodable 的 cast value 参数。
- 当您通过 JSONEncoder 的容器之一 encode(…) 一个值时,它最终会将该值装箱以进行编码。由于所有的编码方法都是通用的,它知道被编码的内容的类型,并且可以拦截它以应用编码策略。您可以在 box_ 的实现中看到这一点:它检查要应用的特定类型。但是,当您执行 value.encode(to: encoder) 时,您会反转关系,并直接调用底层类型实现。
- 例如,对于 Date,您最终总是将日期编码为 Double,因为默认情况下 Date 就是这样编码的(实际上,代码会询问”日期,请将自己编码到编码器中”,而不是”编码器,请编码这个日期”)。 encoder 从来没有看到有一个 Date 因为 Date.encode 是直接调用的,它只是对时间间隔值进行编码。
- 您可以在此要点中看到此行为:将日期package在 AnyEncodable 中会丢失类型上下文,因此编码器无法应用 DateEncodingStrategy。如果您不使用策略,那么这当然完全可以。它只是一个需要注意的警告,因此您最终不会在同一编码的有效负载中出现冲突的值格式。
- @ItaiFerber 感谢您指出这一点!我什至不知道编码器存在这种行为。但是无论如何,变异编码器是一个坏主意。为什么不引入两个概念装饰器,如 StandardDate 和 FormattedDate 以在编码器上以不同的方式打印日期。他们可以在 encode 方法中更改编码器的状态,应用自己并将编码器返回到其原始状态。
- 通常在正确性(即不破坏封装)和有用性之间进行权衡。 JSON 经常被发送到对日期格式有严格要求的服务器(因为 JSON 没有指定日期必须如何编码,每个服务器都是不同的),而且很多时候,您需要对您不拥有且无法影响的类型进行编码— 如果这些类型编码 Dates 而不是 StandardDate 或 FormattedDate,那么您无能为力。由于这种权衡,我们为非常有限的一组类型(目前只有 Date 和 Data)提供这些策略。
- @ItaiFerber 我明白你的推理,但是通过引入打破封装的概念,我们创建了一个积极的反馈循环,我们让开发人员偷工减料,而不是尝试设计方便的 OO 概念来解决严格封装设计的限制。
如果你想定义你的结构符合Codable,你可以这样做:
|
1
2 3 4 5 6 7 8 |
struct Model: Codable {
var param1: String var param2: Int } let model = Model(param1:”test”, param2: 0) |
如果您设置 params: [String: Any],它实际上不会起作用,因为编码器/解码器不知道如何编码/解码 Any,但他们可以为原始类型做到这一点。
如果您需要更多帮助,您应该阅读更多关于新的 Codable 协议的信息。我推荐这个:https://hackernoon.com/everything-about-codable-in-swift-4-97d0e18a2999
- Timofey Solonins 的回答显示了一种封装 Encodable Any 的好方法,以防您想使用它。但是,您最好准确定义模型使用的类型,这样您就不需要封装它们。
来源:https://www.codenong.com/48544098/
