I want to encode an optional field with Swift's JSONEncoderusing a struct that conforms to the Encodable protocol.
The default setting is that JSONEncoder uses the encodeIfPresent method, which means that values that are nil are excluded from the Json.
How can I override this for a single property without writing my custom encode(to encoder: Encoder) function, in which I have to implement the encoding for all properties (like this article suggests under "Custom Encoding" )?
Example:
struct MyStruct: Encodable {
    let id: Int
    let date: Date?
}
let myStruct = MyStruct(id: 10, date: nil)
let jsonData = try JSONEncoder().encode(myStruct)
print(String(data: jsonData, encoding: .utf8)!) // {"id":10}
                A null value (no string) is treated as nil by default so the decoding is supposed to succeed if the property is optional. By the way: You can omit the CodingKeys. If the name of the properties are the same as the keys you don't need explicit CodingsKeys .
The Codable protocol in Swift is really a union of two protocols: Encodable and Decodable . These two protocols are used to indicate whether a certain struct, enum, or class, can be encoded into JSON data, or materialized from JSON data.
A type that can be used as a key for encoding and decoding.
import Foundation
enum EncodableOptional<Wrapped>: ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
    init(nilLiteral: ()) {
        self = .none
    }
}
extension EncodableOptional: Encodable where Wrapped: Encodable {
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .none:
            try container.encodeNil()
        case .some(let wrapped):
            try wrapped.encode(to: encoder)
        }
    }
}
extension EncodableOptional{
    var value: Optional<Wrapped> {
        get {
            switch self {
            case .none:
                return .none
            case .some(let v):
                return .some(v)
            }
        }
        set {
            switch newValue {
            case .none:
                self = .none
            case .some(let v):
                self = .some(v)
            }
        }
    }
}
struct User: Encodable {
    var name: String
    var surname: String
    var age: Int?
    var gender: EncodableOptional<String>
}
func main() {
    var user = User(name: "William", surname: "Lowson", age: 36, gender: nil)
    user.gender.value = "male"
    user.gender.value = nil
    print(user.gender.value ?? "")
    let jsonEncoder = JSONEncoder()
    let data = try! jsonEncoder.encode(user)
    let json = try! JSONSerialization.jsonObject(with: data, options: [])
    print(json)
    let dict: [String: Any?] = [
        "gender": nil
    ]
    let d = try! JSONSerialization.data(withJSONObject: dict, options: [.prettyPrinted])
    let j = try! JSONSerialization.jsonObject(with: d, options: [])
    print(j)
}
main()
This will give you output after executing main:
{
    age = 36;
    gender = "<null>";
    name = William;
    surname = Lowson;
}
{
    gender = "<null>";
}
So, you can see that we encoded gender as it'll be null in dictionary. The only limitation you'll get is that you'll have to access optional value via value property
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With