Desserialização é o processo de restaurar um objeto a partir de um fluxo de dados JSON, XML ou Protobuf, essencial para qualquer aplicativo móvel que trabalhe com uma API remota. De acordo com Apple Developer (2026), o manuseio incorreto de dados recebidos continua sendo uma das causas comuns de falhas em dispositivos. JSONDecoder no iOS e Gson no Android são ferramentas padrão, mas cada uma tem suas próprias características e limitações.
Principais pontos
Desserialização é o processo de converter um fluxo de bytes ou texto estruturado em um objeto de linguagem de programação. No desenvolvimento móvel, esse processo ocorre toda vez que um aplicativo recebe uma resposta do servidor: uma string JSON se torna uma instância da classe User, Order ou Product. A estabilidade das telas que exibem dados ao usuário depende diretamente da correção da desserialização.
Serialização e desserialização são processos mutuamente inversos, raramente simétricos na prática. Serialização converte um objeto em uma string para envio ao servidor, enquanto a desserialização restaura o objeto a partir da string recebida. O servidor pode enviar um campo que não existe no modelo do cliente, usar um formato de data diferente ou retornar null em vez de um número. De acordo com Square Engineering (2025), a assimetria de formatos causa 23% dos erros da camada de rede em aplicativos Android. Para reduzir o risco, utilizam-se versionamento de esquema e especificação de contrato rigorosa via OpenAPI.
JSON continua sendo o formato mais popular para APIs móveis devido à sua legibilidade humana e suporte nativo. Protobuf do Google é usado em sistemas de alta carga — é 3 a 6 vezes mais compacto que JSON e analisa mais rápido, mas requer geração de código a partir de arquivos .proto e não é legível sem ferramentas. XML é menos comum em aplicativos móveis modernos, embora seja usado em serviços SOAP de sistemas empresariais e arquivos de configuração do Android. MessagePack é um formato binário semelhante ao JSON em estrutura, porém mais compacto, popular em sistemas de tempo real.
O processo de desserialização passa por três estágios. Primeiro, a tokenização divide o texto bruto em tokens: chaves, strings, números e delimitadores. Em seguida, a análise sintática verifica a correção da estrutura — se os colchetes estão fechados, se o tipo de aspas está correto, se o formato está em conformidade com RFC 8259. O estágio final é o mapeamento para o modelo de objetos do aplicativo, onde cada chave JSON recebe uma propriedade de classe considerando a estratégia de nomenclatura.
Duas abordagens de mapeamento surgiram no desenvolvimento móvel. Reflection (Gson, JSONSerialization) analisa a estrutura da classe em tempo de execução via Java Reflection API ou Objective-C runtime — é flexível e não requer configuração adicional, mas é mais lento e consome mais memória. Code generation (Moshi codegen, kotlinx.serialization, Codable) gera código em tempo de compilação: mais rápido, seguro em termos de tipos e não expõe a estrutura interna através de reflection. JetBrains e Square recomendam code generation para builds de produção — os ganhos de desempenho chegam a 2-4 vezes nos benchmarks do Google.
struct User: Codable {
let id: Int
let name: String
let email: String
let createdAt: Date
}
let json = """
{
"id": 42,
"name": "Alice",
"email": "alice@example.com",
"created_at": "2026-06-01T12:00:00Z"
}
"""
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let user = try decoder.decode(User.self, from: data)
Exemplo de desserialização de JSON em um modelo User em Swift. A estratégia convertFromSnakeCase converte automaticamente chaves de API em snake_case para propriedades do modelo em camelCase — uma prática padrão em projetos iOS. O parâmetro data são os bytes brutos da resposta do servidor obtidos via URLSession. O tratamento de erros através de try permite capturar JSON malformado sem travar o aplicativo.
JSONDecoder suporta quatro estratégias de chaves: useDefaultKeys (correspondência exata), convertFromSnakeCase (snake_case → camelCase), custom (closure) e convertFromKebabCase (kebab-case → camelCase). Para datas, estão disponíveis .iso8601, .secondsSince1970, .millisecondsSince1970 e um dateFormatter personalizado. Escolher a estratégia correta é o primeiro passo para uma desserialização robusta, prevenindo a maioria dos erros de incompatibilidade de formatos.
JSONDecoder é o mecanismo padrão de desserialização no SDK do iOS, funcionando com o protocolo Codable. JSONDecoder analisa automaticamente JSON em instâncias de struct ou class, suportando objetos aninhados, arrays e primitivos. Para lógica personalizada, utiliza-se o método init(from: Decoder) — ele permite lidar com formatos não padronizados, campos ausentes em uma versão antiga da API ou combinar várias chaves JSON em uma única propriedade.
struct Order: Decodable {
let orderId: String
let amount: Double
let status: OrderStatus
enum OrderStatus: String, Decodable {
case pending, confirmed, shipped, cancelled
}
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let order = try decoder.decode(Order.self, from: jsonData)
DateDecodingStrategy determina como o JSONDecoder interpreta strings de data. .iso8601 é o mais utilizado — o formato padrão de APIs REST. O enum aninhado OrderStatus é decodificado automaticamente a partir de valores de string JSON. Isso evita números mágicos e torna o código autodocumentado — o status do pedido sempre tem um conjunto estritamente definido de valores.
Desde o Swift 4.2, o Codable suporta property wrappers para desserialização personalizada de propriedades individuais. @DefaultValue é um wrapper popular que define um valor padrão se o campo estiver ausente no JSON. @LosslessString converte uma string em número e vice-versa. Isso é especialmente útil quando o servidor envia um id como a string "123" mas o modelo espera um Int. Property wrappers reduzem código boilerplate em init(from:) e tornam os modelos mais limpos.
No Android, a escolha da biblioteca de desserialização depende do idioma e dos requisitos do projeto. Gson do Google é a opção mais comum, funcionando via reflection, mas com problemas de desempenho em hierarquias complexas. Moshi da Square suporta tanto reflection quanto code generation, consumindo menos memória e processando grandes respostas mais rapidamente. kotlinx.serialization da JetBrains é uma solução nativa Kotlin com integração ao compilador que não usa reflection.
@Serializable
data class User(
@SerialName("user_id")
val userId: Int,
val name: String,
val email: String,
@SerialName("created_at")
val createdAt: String
)
val json = Json { ignoreUnknownKeys = true }
val user = json.decodeFromString<User>(response)
@Serializable é uma anotação do compilador Kotlin que ativa a geração de código para a classe. O parâmetro ignoreUnknownKeys evita falhas se o servidor enviar um campo ausente no modelo. Para mapear chaves snake_case, usa-se @SerialName — o equivalente ao convertFromSnakeCase do iOS. De acordo com JetBrains (2026), a biblioteca suporta multiplataforma: a mesma classe Serializable funciona no Android, iOS (KMP) e Kotlin do lado do servidor.
A escolha entre bibliotecas se resume a um compromisso entre velocidade e flexibilidade. Gson é bom para protótipos e projetos Java — não requer anotações e funciona imediatamente. Moshi ocupa uma posição intermediária: codegen via @JsonClass(generateAdapter = true) oferece velocidade próxima à kotlinx.serialization, enquanto o modo reflection oferece a flexibilidade do Gson. kotlinx.serialization é a opção mais rápida para projetos Kotlin puros, mas requer Kotlin 1.4+ e o plugin Kotlin Serialization no Gradle.
| Biblioteca | Mecanismo | Velocidade | KMP |
|---|---|---|---|
| Gson | Reflection | Baixa | Não |
| Moshi | Reflection / Codegen | Média / Alta | Não |
| kotlinx.serialization | Codegen do compilador | Alta | Sim |
Type mismatch é uma situação em que o JSON contém um valor de um tipo, mas o modelo espera outro. O servidor enviou a string "42" em vez de um número, ou o número 1 em vez de true booleano. No iOS, o JSONDecoder lançará DecodingError.typeMismatch por padrão; no Android, o Gson tentará converter, enquanto Moshi e kotlinx.serialization exigem adaptadores explícitos. A solução é usar estratégias lenient ou desserializadores personalizados para campos específicos.
Quando o servidor não inclui um campo opcional, o código falha com um erro. Campos Optional em Swift e tipos nullable em Kotlin resolvem o problema: se o campo é null ou está ausente no JSON, a propriedade recebe nil/null e o aplicativo continua funcionando. Para campos obrigatórios, vale verificar sua presença no nível do cliente API antes da desserialização. Moshi e kotlinx.serialization exigem todos os campos por padrão — a marcação nullable e valores padrão removem essa restrição.
Alterações na estrutura JSON no servidor são uma fonte comum de falhas em produção. A prática padrão é o versionamento de esquema através de um campo version no objeto raiz e suporte a 2-3 versões anteriores no cliente. kotlinx.serialization permite declarar vários modelos para diferentes versões e selecionar o correto com base no campo version após a análise inicial em JsonElement. Proteção adicional inclui ignoreUnknownKeys para novos campos e valores padrão para campos que podem ser removidos.
| Erro | Sintoma | Biblioteca com proteção |
|---|---|---|
| Type mismatch | DecodingError / Exceção | kotlinx — coerceInputValues = true |
| Campo ausente | Falha ao acessar | Moshi — @Transient + default |
| Formato de data incorreto | Erro de decodificação | JSONDecoder — dateDecodingStrategy |
| Campos extras | Ignorados ou falha | kotlinx — ignoreUnknownKeys = true |
| Null em campo não-null | Falha em runtime | Moshi — lenient com @Nullable |
Registro de erros de desserialização é uma prática obrigatória em produção. Envolva decode em do/catch, registre o JSON bruto e o tipo de modelo esperado no Crashlytics ou Sentry. Isso permitirá identificar rapidamente qual campo de qual API quebrou e em qual versão do aplicativo. Sem registro, um erro de desserialização parece uma falha misteriosa sem contexto.
Perguntas frequentes
Parsing é a análise de texto estruturado em elementos constituintes sem necessariamente criar um modelo tipado. A desserialização é um caso específico de parsing cujo resultado é um objeto de linguagem completo com tipos de propriedades conhecidos. O parsing pode ser por fluxo, a desserialização sempre cria um objeto completo.
Para um projeto em Kotlin puro, recomenda-se kotlinx.serialization — é integrada ao compilador, não usa reflection e suporta Kotlin Multiplatform. Para um projeto Java existente — Moshi com code generation. Gson é melhor deixar para projetos legados onde sua substituição exigiria esforço significativo.
No iOS, use keyDecodingStrategy = .convertFromSnakeCase no JSONDecoder. No Android com kotlinx.serialization, use @SerialName para cada campo. No Moshi, aplique @Json(name="field_name") ou um JsonAdapter.Factory global. Um estilo consistente em nível de projeto é uma boa prática acordada no contrato da API.
A razão mais comum é um null inesperado do servidor em um campo declarado como obrigatório. Em desenvolvimento, o servidor retorna dados completos; em produção, retorna uma resposta reduzida. A solução: marcar todos os campos potencialmente ausentes como nullable (Kotlin) ou optional (Swift), usar ignoreUnknownKeys e valores padrão.
Code generation (Moshi codegen, kotlinx.serialization, Codable) é 2 a 4 vezes mais rápida que reflection nos benchmarks do Google. Além da velocidade, a geração de código é mais segura em tipos, não requer metadados de classe em runtime e erros de tipo são detectados em tempo de compilação, não durante a desserialização.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também