我用的是Swift 4的JSONEncoder.我有一个带可选属性的Codable struct ,我希望这个属性在生成的JSON数据中显示为null值,当值为nil时.但是,JSONEncoder会丢弃该属性,不会将其添加到JSON输出中.有没有办法将JSONEncoder配置为保留密钥,并在这种情况下将其设置为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)!)

推荐答案

是的,但是您必须编写您自己的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处打开一个缺陷,要求在JSONEncoding上添加一个新的OptionalEncodingStrategy标志,以匹配现有的DateEncodingStrategy,以此类推.(请参阅下面的原因,为什么这可能无法在今天的SWIFT中实际实施,但随着SWIFT的发展,进入跟踪系统仍然很有用.)


编辑:对于保罗下面的问题,这将发送到通用encode<T: Encodable>版本,因为Optional符合Encodable.这是通过以下方式实现的:

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处理要比在我们自己的编码器中将它们作为特例处理并自己调用encodeNil要好.

另一个显而易见的问题是,它为什么会以这种方式工作.既然Optional是可编码的,并且生成的可编码一致性对所有属性进行编码,为什么"手工编码所有属性"的工作方式不同?答案是一致性生成器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中可能真的很难使其可配置……)

Ios相关问答推荐

SwiftUI绑定到特定的一个或多个枚举 case

设置托管在DigitalOcean上的iOS通用链接

如何在iOS 17中拦截UITextView中的链接点击?UITextItemInteraction已弃用

OnTapGesture在其修改的视图外部触发

如何创建装饰视图?

为什么applicationState发布者只发布一次?

iOS应用启动时崩溃问题解决方法-NO_CRASH_STACK-DYLD 4个符号丢失

ionic 4 - 编译 ios - 来自项目Pods的目标GoogleDataTransport中的问题

ITMS-90725 无用的错误信息,因为它自相矛盾,苹果上传

在 SwiftUI 中放置全局 NavigationPath 的位置

Swift/iOS:泛型错误:协议需要嵌套类型V;你想添加它吗?

具有线性渐变的 SVG textPath 在 Safari 中损坏

视觉扫描无法识别 iOS16 上的 GS1 条码特殊字符

iOS - 确保在主线程上执行

在 iOS 8.1 模拟器上更改语言不起作用

水平滚动 UICollectionView 时对齐单元格的中心

无法加载从带有标识符的包中的笔尖引用的图像

如何在 Swift 中将新单元格插入 UITableView

为什么 Xcode 4 不创建任何产品?

从 Swift 转换为 Objective-c 的工具