是的,但您必须编写自己的 encode(to:) 实现,您不能使用自动生成的实现。
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)
}
}
直接编码一个可选项将编码一个空值,就像你正在寻找的那样。
如果这对您来说是一个重要的用例,您可以考虑在bugs.swift.org 打开一个缺陷,要求在 JSONEncoder 上添加一个新的OptionalEncodingStrategy 标志以匹配现有的DateEncodingStrategy 等。(见下文为什么这在今天可能无法在 Swift 中实际实现,但随着 Swift 的发展,进入跟踪系统仍然有用。)
编辑:对于 Paulo 的以下问题,这将发送到通用 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 将Optionals 作为另一个Encodable 处理比在我们自己的编码器中将它们视为特殊情况并自己调用encodeNil 更好。
另一个明显的问题是为什么它首先以这种方式工作。既然 Optional 是 Encodable,并且生成的 Encodable 一致性编码了所有属性,为什么“手动编码所有属性”的工作方式不同呢?答案是一致性生成器includes a special case for Optionals:
// 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 中可能很难进行可配置......)