O JSON – JavaScript Object Notation, é um formato de dados utilizado para intercâmbio de dados computacionais que é empregado na maioria das aplicações atuais e é definido pela Json.org.
Existem no mercado vários frameworks, e para diversas linguagens, que ajudam na conversão de objetos Json para seu modelo de dados porém nesse post vamos focar em apenas um deles, o ObjectMapper disponível para swift.
Para começar vamos criar o nosso modelo de dados de um usuário de nosso sistema e que deverá ser enviado para nosso servidor.
class User { var id = 0 var active = true var updateAt = Date() var name = String() var state: State? var cpf = String() }
No código criamos um classe que representa o nosso usuário com seu nome, estado e cpf, também foi incluído controladores para saber qual identificador do usuário (id) qual a data da última modificação e se o mesmo está ativo no sistema.
Finalizado o mapeamento do nosso modelo agora vamos utilizar o framework para mapear o objeto para o formato Json e para isso primeiramente vamos importar a biblioteca no nosso arquivo modelo.
import ObjectMapper
Após realizar o import vamos criar uma extensão da classe User e assinar o protocolo “Mappable” do framework que será responsável pela conversão dos dados.
import Foundation import ObjectMapper class User { var id = 0 var active = true var updateAt = Date() var name = String() var state: State? var cpf = String() required init?(map: Map) { } } // MARK: Mappable extension User: Mappable { func mapping(map: Map) { } }
A implementação do protocolo Mappable exige a implementação do construtor, que por ser requerido não pode ficar na extensão, e da função mapping responsável pelo mapeamento do objeto. Agora basta mapear o objeto, vinculando cada propriedade do modelo com uma chave específica em Json, dentro da função mapping. Observe que temos no nosso modelo um objeto aninhado, o estado, que também deverá implementar o protocolo Mappable para realizar a conversão corretamente.
func mapping(map: Map) { id <- map["id"] active <- map["active"] updateAt <- map["updated_at"] name <- map["name"] state <- map["state"] cpf <- map["cpf"] }
Finalizado o mapeamento das propriedades do nosso modelo basta chamar a função “ToJson()”, disponibilizada pela implementação do protocolo Mappable, que teremos o seguinte resultado:
{ "id": 0, "active": true "updateAt": "Oct 12, 2015, 4:49 PM" "cpf": "111.111.211-11", "state": { "id": 1 "name": "São Paulo" } }
Apesar do objeto estar correto do ponto de vista do protocolo JSON com relação aos nossos dados ainda temos alguns pontos a resolver, como por exemplo a data, que esta no formato padrão de impressão e que não é compatível com o formato aceito por nosso servidor.
Nesse caso precisamos não apenas mapear nossos dados mas também transforma-lo para o formato correto aceito pelo servidor que é: 2016-10-18T21:00:57.431-0200, e para isso o framework possui um protocolo que permite implementar a transformação chamado “Transform”.
public protocol TransformType { associatedtype Object associatedtype JSON func transformFromJSON(_ value: Any?) ->; Object? func transformToJSON(_ value: Object?) ->; JSON? }
No protocolo é possível ver que temos dois tipos associonados sendo Object o tipo do seu Modelo e JSON o tipo que será retornado após a transformação e duas funções responsáveis pela transformação propriamente dita.
Vamos criar uma estrutura chamada ServerDateTransform que assinar o protocolo para implementar as transformações necessárias. No nosso caso o tipo associado ao Object será o Date e ao JSON o resultado final será string (data convertida para o formato correto).
import Foundation import ObjectMapper struct ServerDateTransform: TransformType { public typealias Object = Date public typealias JSON = String public init() {} public func transformFromJSON(_ value: Any?) -> Date? { return nil } public func transformToJSON(_ value: Date?) -> String? { return nil } }
Com nossa estrutura criada vamos implementar a transformação da data utilizando o DateTransform do iOS para imprimir a data no formato correto que precisamos, vamos criar uma variável computada que retorna um DataTransform com o formato desejado.
fileprivate var dateFormatter: DateFormatter { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" return dateFormatter }
E por fim implementar as funções de transformações, resultando no seguinte código.
import Foundation import ObjectMapper struct ServerDateTransform: TransformType { public typealias Object = Date public typealias JSON = String public init() {} fileprivate var dateFormatter: DateFormatter { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" return dateFormatter } public func transformFromJSON(_ value: Any?) -> Date? { if let strDate = value as? String { return dateFormatter.date(from: strDate) } return nil } public func transformToJSON(_ value: Date?) -> String? { if let date = value { return dateFormatter.string(from: date) } return nil } }
Se atente para o fato que a função tem como parâmetro uma objeto nullable e nesse caso devemos sempre validar se a entrada é válida para evitar erros futuros.
Agora basta aplicar a nova estrutura na propriedade de data da função “mapping” do modelo para realizar a transformação correta.
func mapping(map: Map) { id <- map["id"] active <- map["active"] updateAt <- (map["updated_at"], ServerDateTransform()) name <- map["name"] state <- map["crm_state_id"] cpf <- map["cpf"] }
Note que o operador <- pode receber apenas um map com a chave do JSON ou uma tupla com a chave e o transformação necessária.
Aplicando a transformação temos a seguinte saída:
{ "id": 0, "active": true "updateAt": "2016-10-18T21:00:57.431-0200" "cpf": "111.111.211-11", "state": { "id": 1 "name": "São Paulo" } }
Com isso podemos implementar qualquer transformação de dados necessário para comunicação entre nossos sistemas.