How to exclude properties from Swift Codable?
The list of keys to encode/decode is controlled by a type called CodingKeys
(note the s
at the end). The compiler can synthesize this for you but can always override that.
Let's say you want to exclude the property nickname
from both encoding and decoding:
struct Person: Codable { var firstName: String var lastName: String var nickname: String? private enum CodingKeys: String, CodingKey { case firstName, lastName }}
If you want it to be asymmetric (i.e. encode but not decode or vice versa), you have to provide your own implementations of encode(with encoder: )
and init(from decoder: )
:
struct Person: Codable { var firstName: String var lastName: String // Since fullName is a computed property, it's excluded by default var fullName: String { return firstName + " " + lastName } private enum CodingKeys: String, CodingKey { case firstName, lastName, fullName } // We don't want to decode `fullName` from the JSON init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) firstName = try container.decode(String.self, forKey: .firstName) lastName = try container.decode(String.self, forKey: .lastName) } // But we want to store `fullName` in the JSON anyhow func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(firstName, forKey: .firstName) try container.encode(lastName, forKey: .lastName) try container.encode(fullName, forKey: .fullName) }}
Another way to exclude some properties from encoder, separate coding container can be used
struct Person: Codable { let firstName: String let lastName: String let excludedFromEncoder: String private enum CodingKeys: String, CodingKey { case firstName case lastName } private enum AdditionalCodingKeys: String, CodingKey { case excludedFromEncoder } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let anotherContainer = try decoder.container(keyedBy: AdditionalCodingKeys.self) firstName = try container.decode(String.self, forKey: .firstName) lastName = try container.decode(String.self, forKey: .lastName) excludedFromEncoder = try anotherContainer(String.self, forKey: . excludedFromEncoder) } // it is not necessary to implement custom encoding // func encode(to encoder: Encoder) throws // let person = Person(firstName: "fname", lastName: "lname", excludedFromEncoder: "only for decoding") // let jsonData = try JSONEncoder().encode(person) // let jsonString = String(data: jsonData, encoding: .utf8) // jsonString --> {"firstName": "fname", "lastName": "lname"}}
same approach can be used for decoder
If we need to exclude decoding of a couple of properties from a large set of properties in the structure, declare them as optional properties. Code to unwrapping optionals is less than writing a lot of keys under CodingKey enum.
I would recommend using extensions to add computed instance properties and computed type properties. It separates codable comforming properties from other logic hence provides better readability.