可以,但你需要编写自己的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)
}
}
将可选项直接编码将会编码一个null,就像你所期望的那样。
如果这对你来说是一个重要的用例,你可以考虑在bugs.swift.org上开启一个缺陷,请求在JSONEncoder上添加一个新的OptionalEncodingStrategy
标志,以匹配现有的DateEncodingStrategy
等。(请看下面为什么这在Swift中很难实现,但在Swift不断发展的过程中进入跟踪系统仍然是有用的。)
编辑:回答Paulo下面的问题,这个分派到通用的encode<T: Encodable>
版本,因为Optional
符合Encodable
。这是在Codable.swift中实现的。
extension Optional : Encodable /* where Wrapped : Encodable */ {
@_inlineable
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
的调用,我认为让标准库把Optionals作为另一种可编码类型来处理,比我们自己在编码器中将它们视为特殊情况并调用encodeNil
更好。
另一个显而易见的问题是,为什么它首先可以按照这种方式工作。由于Optional是可编码的,生成的可编码符合对所有属性进行编码的要求,为什么手动“编码所有属性”会有所不同?答案是符合生成器Optionals的特殊情况:
...
if (varType->getAnyNominal() == C.getOptionalDecl() ||
varType->getAnyNominal() == C.getImplicitlyUnwrappedOptionalDecl()) {
methodName = C.Id_encodeIfPresent;
}
这意味着更改此行为需要更改自动生成的符合性,而不是
JSONEncoder
(这也意味着在当今的 Swift 中很难进行配置...)。