【问题标题】:Swift Encodable: encode nil as an empty objectSwift Encodable:将 nil 编码为空对象
【发布时间】:2021-09-27 13:15:03
【问题描述】:

如何将 nil 属性编码为空 JSON 对象?

struct Foo: Encodable {
    let id = 10
    let bar: Bar? = nil
}

struct Bar: Encodable {
    let number: Int
}

let data = try! JSONEncoder().encode(Foo())

print(String(data: data, encoding: .utf8)!)

打印出来:

"{"id":7}"

我想要的是:

"{"id":7, "bar":{}}"

【问题讨论】:

  • 这基本上会破坏编码和解码之间的对称性,想知道需要这种编码的用例是什么。
  • let bar: Bar? = nil 这是毫无意义的。 bar 永远是nil
  • 用 NSNull 编码不是更好吗?
  • @LeoDabus 这段代码只是为了展示我的问题。
  • @Cristik 我正在使用的 API 要求我这样做。我同意这不是传统的,但我别无选择。而且我只是对其进行编码,从不从 JSON 中解码。

标签: json swift codable foundation encodable


【解决方案1】:

bar = nil 时,您可以向encoder 引入一个没有属性的空结构体

struct Foo: Encodable {
    let id = 10
    let bar: Bar? = nil
    
    enum CodingKeys : String, CodingKey {
        case id
        case bar
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        
        if let bar = bar {
            try container.encode(bar, forKey: .bar)
        }
        else {
            try container.encode(Empty(), forKey: .bar)
        }
    }
}

struct Bar: Encodable {
    let number: Int
}

struct Empty: Encodable {
}

【讨论】:

  • 你比我先到了那里。哈哈!只是输入了一个几乎完全一样的答案。 ?
  • @Fogmeister 我想我发布答案的速度快了几秒钟 :)
【解决方案2】:

Foo 实现自定义encode(to:),如果Bar 为nil,则使用空字典

struct Foo: Encodable {
    let id = 10
    let bar: Bar? = nil

    enum CodingKeys: String, CodingKey {
        case id, bar
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        switch bar {
        case .some(let value):
            try container.encode(value, forKey: .bar)
        case .none:
            try container.encode([String: Bar?](), forKey: .bar)
        }
    }
}

【讨论】:

    【解决方案3】:

    不知道你为什么需要这个,因为通常不会对解码失败的表单进行编码。

    不过,如果您发现自己在多个地方都需要这种逻辑,您可以使用这种功能扩展 KeyedEncodingContainer

    extension KeyedEncodingContainer {
        mutating func encodeOptional<T: Encodable>(_ value: T?, forKey key: Self.Key) throws {
            if let value = value { try encode(value, forKey: key) }
            else { try encode([String:String](), forKey: key) }
        }
    }
    

    ,然后在Foo中实现encode(to:)方法:

    struct Foo: Encodable {
        let id = 10
        let bar: Bar? = nil
        
        enum CodingKeys: String, CodingKey {
            case id
            case bar
        }
        
        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(id, forKey: .id)
            try container.encodeOptional(bar, forKey: .bar)
        }
    }
    

    如果您发现自己需要为其他类型的容器中的 nil 值编码空 JSON 对象,也可以使用类似的 encodeOptional 方法扩展 UnkeyedDecodingContainerSingleValueDecodingContainer

    【讨论】:

    • 这看起来很干净!我需要这个的唯一原因是因为我使用的 API 要求我这样做......
    猜你喜欢
    • 2018-08-03
    • 1970-01-01
    • 2019-03-13
    • 2018-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多