使用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)