Los nuevos protocolos Encodable
/ de Swift 4 Decodable
hacen que la (des) serialización de JSON sea bastante agradable. Sin embargo, todavía no he encontrado una manera de tener un control detallado sobre qué propiedades deben codificarse y cuáles deben decodificarse.
He notado que excluir la propiedad de la CodingKeys
enumeración adjunta excluye la propiedad del proceso por completo, pero ¿hay alguna manera de tener un control más detallado?
CodingKeys
enumeración es suficiente.Codable
protocolo (init(from:)
yencode(to:)
) manualmente para un control total sobre el proceso.Respuestas:
La lista de claves para codificar / decodificar está controlada por un tipo llamado
CodingKeys
(observe els
al final). El compilador puede sintetizar esto por usted, pero siempre puede anularlo.Supongamos que desea excluir la propiedad
nickname
tanto de la codificación como de la decodificación:struct Person: Codable { var firstName: String var lastName: String var nickname: String? private enum CodingKeys: String, CodingKey { case firstName, lastName } }
Si desea que sea asimétrico (es decir, codificar pero no decodificar o viceversa), debe proporcionar sus propias implementaciones de
encode(with encoder: )
yinit(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 case lastName case fullName } 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) } 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) } }
fuente
nickname
un valor predeterminado para que esto funcione. De lo contrario, no hay ningún valor que se pueda asignar a la propiedad eninit(from:)
.encode
en el ejemplo asimétrico? Dado que ese sigue siendo el comportamiento estándar, no pensé que fuera necesario. Solodecode
porque de ahí es de donde viene la asimetría.fullName
que no se puede asignar a una propiedad almacenada, debe proporcionar un codificador y descodificador personalizados.Si necesitamos excluir la decodificación de un par de propiedades de un gran conjunto de propiedades en la estructura, declárelas como propiedades opcionales. El código para desenvolver opcionales es menor que escribir muchas claves en CodingKey enum.
Recomendaría usar extensiones para agregar propiedades de instancia calculadas y propiedades de tipo calculado. Separa las propiedades de conformidad codificables de otras lógicas, por lo que proporciona una mejor legibilidad.
fuente
Otra forma de excluir algunas propiedades del codificador, se puede usar un contenedor de codificación separado
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"} }
se puede utilizar el mismo enfoque para el decodificador
fuente
Puede utilizar propiedades calculadas:
struct Person: Codable { var firstName: String var lastName: String var nickname: String? var nick: String { get { nickname ?? "" } } private enum CodingKeys: String, CodingKey { case firstName, lastName } }
fuente
lazy var
propiedad de tiempo de ejecución que la convierte efectivamente la excluyó de Codable.Si bien esto se puede hacer, en última instancia, termina siendo muy poco inteligente e incluso poco inteligente . Creo que veo de dónde viene, el concepto de
#id
s prevalece en HTML, pero rara vez se transporta al mundoJSON
que considero algo bueno (TM).Algunas
Codable
estructuras podrán analizar suJSON
archivo sin problemas si lo reestructura utilizando hashes recursivos, es decir, sirecipe
solo contiene una matrizingredients
que a su vez contiene (uno o varios)ingredient_info
. De esa manera, el analizador te ayudará a unir tu red en primer lugar y solo tendrás que proporcionar algunos backlinks a través de un simple recorrido de la estructura si realmente los necesitas . Dado que esto requiere una reelaboración completa de su estructura de datosJSON
y la suya, solo esbozo la idea para que la piense. Si lo considera aceptable, por favor dígame en los comentarios, entonces podría desarrollarlo más, pero dependiendo de las circunstancias, es posible que no tenga la libertad de cambiar ninguno de ellos.fuente
He usado el protocolo y su extensión junto con AssociatedObject para establecer y obtener la propiedad de la imagen (o cualquier propiedad que deba excluirse de Codable).
Con esto no tenemos que implementar nuestro propio codificador y decodificador
Aquí está el código, manteniendo el código relevante para simplificar:
protocol SCAttachmentModelProtocol{ var image:UIImage? {get set} var anotherProperty:Int {get set} } extension SCAttachmentModelProtocol where Self: SCAttachmentUploadRequestModel{ var image:UIImage? { set{ //Use associated object property to set it } get{ //Use associated object property to get it } } } class SCAttachmentUploadRequestModel : SCAttachmentModelProtocol, Codable{ var anotherProperty:Int }
Ahora, siempre que queramos acceder a la propiedad Image podemos usar en el objeto confirmando el protocolo (SCAttachmentModelProtocol)
fuente