2

如何将 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":{}}"

4

3 回答 3

3

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

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 {
}
于 2021-09-27T14:02:01.310 回答
2

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

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)
        }
    }
}
于 2021-09-27T15:16:18.760 回答
1

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

尽管如此,如果您发现自己在多个地方都需要这种逻辑,您可以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) }
    }
}

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

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 对象,您也可以使用类似的方法UnkeyedDecodingContainer进行扩展SingleValueDecodingContainerencodeOptional

于 2021-09-28T05:18:33.120 回答