使用JSONEncoder将nil值编码为null

我正在使用Swift 4的JSONEncoder 。 我有一个可选属性的Codable结构,我想这个属性显示为null值生成的JSON数据时,值nil 。 但是, JSONEncoder会丢弃该属性,并不会将其添加到JSON输出中。 有没有办法configurationJSONEncoder以便它保留的关键,并将其设置为null在这种情况下?

下面的代码片段产生{"number":1} ,但我宁愿它给我{"string":null,"number":1}

 struct Foo: Codable { var string: String? = nil var number: Int = 1 } let encoder = JSONEncoder() let data = try! encoder.encode(Foo()) print(String(data: data, encoding: .utf8)!) 

是的,但是你必须编写你自己的编码器。 你不能使用默认的。

 struct Foo: Codable { var string: String? = nil var number: Int = 1 func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(number, forKey: .number) try container.encode(string, forKey: .string) } } 

直接对可选项进行编码将会编码一个null,就像你正在寻找的一样。

如果这对你来说是一个重要的用例,你可以考虑在bugs.swift.org上打开一个缺陷,要求在JSONEncoder上添加一个新的OptionalEncodingStrategy标志,以匹配现有的DateEncodingStrategy等。(请看下面为什么这是不可能的今天在Swift中实际实现,但随着Swift的发展,进入跟踪系统仍然是有用的。)


编辑:对于下面的问题,这将调度到通用encode<T: Encodable>版本,因为Optional符合Encodable 。 这在Codable.swift中是这样实现的:

 extension Optional : Encodable /* where Wrapped : Encodable */ { @_inlineable // FIXME(sil-serialize-all) public func encode(to encoder: Encoder) throws { assertTypeIsEncodable(Wrapped.self, in: type(of: self)) var container = encoder.singleValueContainer() switch self { case .none: try container.encodeNil() case .some(let wrapped): try (wrapped as! Encodable).__encode(to: &container) } } } 

encodeNil的调用包装起来,我认为让stdlib作为另一个Encodable处理Optionals比在我们自己的编码器encodeNil它们视为特殊情况要好,并且自己调用encodeNil

另一个显而易见的问题是,为什么它首先这样工作。 由于可选是Encodable,并且生成的Encodable一致性编码所有属性,为什么“手动编码所有属性”的工作方式不同? 答案是一致性生成器包含一个可选的特殊情况 :

 // Now need to generate `try container.encode(x, forKey: .x)` for all // existing properties. Optional properties get `encodeIfPresent`. ... if (varType->getAnyNominal() == C.getOptionalDecl() || varType->getAnyNominal() == C.getImplicitlyUnwrappedOptionalDecl()) { methodName = C.Id_encodeIfPresent; } 

这意味着改变这种行为将需要改变自动生成的一致性,而不是JSONEncoder (这也意味着它可能真的很难在今天的Swift中进行configuration)