O que e Serialization em Kotlin?
Serialization (serializacao) e o processo de converter objetos Kotlin em um formato que pode ser armazenado ou transmitido (como JSON, Protobuf ou CBOR), e desserializacao e o processo inverso. O kotlinx.serialization e a biblioteca oficial do Kotlin para essa tarefa, gerando codigo de serializacao em tempo de compilacao usando um plugin do compilador.
Diferente de bibliotecas baseadas em reflexao (como Gson), kotlinx.serialization e mais rapida, type-safe e funciona em todas as plataformas Kotlin (JVM, iOS, JavaScript, WASM).
Configuracao do projeto
// build.gradle.kts
plugins {
kotlin("jvm") version "1.9.22"
kotlin("plugin.serialization") version "1.9.22"
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}
Uso basico com JSON
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class Usuario(
val nome: String,
val idade: Int,
val email: String
)
fun main() {
val usuario = Usuario("Ana", 30, "ana@email.com")
// Serializar: objeto -> JSON string
val json = Json.encodeToString(usuario)
println(json)
// {"nome":"Ana","idade":30,"email":"ana@email.com"}
// Desserializar: JSON string -> objeto
val objeto = Json.decodeFromString<Usuario>(json)
println(objeto)
// Usuario(nome=Ana, idade=30, email=ana@email.com)
}
A anotacao @Serializable instrui o plugin do compilador a gerar um serializer para a classe. Sem ela, a classe nao pode ser serializada.
Customizando nomes de campos
@Serializable
data class Produto(
@SerialName("nome_produto")
val nome: String,
@SerialName("preco_unitario")
val preco: Double,
@SerialName("em_estoque")
val emEstoque: Boolean
)
fun main() {
val produto = Produto("Teclado", 299.90, true)
val json = Json.encodeToString(produto)
println(json)
// {"nome_produto":"Teclado","preco_unitario":299.9,"em_estoque":true}
}
Valores opcionais e padrao
@Serializable
data class Configuracao(
val tema: String = "claro",
val fontSize: Int = 14,
val idioma: String = "pt-BR"
)
fun main() {
val json = Json { encodeDefaults = false }
// Serializar: campos com valor padrao podem ser omitidos
val config = Configuracao()
println(json.encodeToString(config))
// {} (campos com valor padrao omitidos)
// Desserializar: campos ausentes usam valor padrao
val configParcial = json.decodeFromString<Configuracao>("""{"tema":"escuro"}""")
println(configParcial)
// Configuracao(tema=escuro, fontSize=14, idioma=pt-BR)
}
Configurando o Json
O objeto Json pode ser customizado com varias opcoes:
val jsonConfig = Json {
prettyPrint = true // JSON formatado com indentacao
isLenient = true // Aceita JSON nao estritamente valido
ignoreUnknownKeys = true // Ignora campos desconhecidos
encodeDefaults = true // Inclui campos com valor padrao
coerceInputValues = true // Coerce valores incorretos para padrao
explicitNulls = false // Omite campos null
namingStrategy = JsonNamingStrategy.SnakeCase // Converte camelCase para snake_case
}
@Serializable
data class Resposta(
val statusCode: Int,
val mensagem: String,
val dados: String? = null
)
fun main() {
val json = """
{
"status_code": 200,
"mensagem": "Sucesso",
"campo_extra": "ignorado"
}
""".trimIndent()
val resposta = jsonConfig.decodeFromString<Resposta>(json)
println(resposta)
}
Serializando colecoes e tipos aninhados
@Serializable
data class Endereco(
val rua: String,
val cidade: String,
val estado: String
)
@Serializable
data class Pedido(
val id: Long,
val itens: List<String>,
val endereco: Endereco,
val tags: Set<String> = emptySet(),
val metadados: Map<String, String> = emptyMap()
)
fun main() {
val pedido = Pedido(
id = 1001,
itens = listOf("Teclado", "Mouse"),
endereco = Endereco("Rua A", "Sao Paulo", "SP"),
tags = setOf("eletronicos", "informatica"),
metadados = mapOf("prioridade" to "alta")
)
val json = Json { prettyPrint = true }
println(json.encodeToString(pedido))
}
Polimorfismo com sealed classes
@Serializable
sealed class Notificacao {
@Serializable
@SerialName("email")
data class Email(val destinatario: String, val assunto: String) : Notificacao()
@Serializable
@SerialName("push")
data class Push(val token: String, val titulo: String) : Notificacao()
@Serializable
@SerialName("sms")
data class Sms(val telefone: String, val mensagem: String) : Notificacao()
}
fun main() {
val notificacoes: List<Notificacao> = listOf(
Notificacao.Email("ana@email.com", "Bem-vinda"),
Notificacao.Push("token123", "Nova mensagem"),
Notificacao.Sms("+5511999999999", "Codigo: 1234")
)
val json = Json { prettyPrint = true }
val jsonStr = json.encodeToString(notificacoes)
println(jsonStr)
// Desserializa com o tipo correto baseado no discriminador
val desserializado = json.decodeFromString<List<Notificacao>>(jsonStr)
desserializado.forEach { println(it::class.simpleName) }
}
Serializer customizado
Para tipos que nao sao controlados por voce ou que precisam de formatacao especial:
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import java.time.LocalDate
object LocalDateSerializer : KSerializer<LocalDate> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: LocalDate) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): LocalDate {
return LocalDate.parse(decoder.decodeString())
}
}
@Serializable
data class Evento(
val nome: String,
@Serializable(with = LocalDateSerializer::class)
val data: LocalDate
)
Outros formatos alem de JSON
kotlinx.serialization suporta multiplos formatos:
// Protobuf
implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.6.2")
@Serializable
data class Mensagem(@ProtoNumber(1) val texto: String, @ProtoNumber(2) val id: Int)
val bytes = ProtoBuf.encodeToByteArray(Mensagem("Ola", 1))
val msg = ProtoBuf.decodeFromByteArray<Mensagem>(bytes)
// CBOR
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.6.2")
val cborBytes = Cbor.encodeToByteArray(usuario)
Quando usar kotlinx.serialization
- APIs REST: serializar e desserializar JSON em chamadas HTTP com Ktor ou Retrofit.
- Armazenamento local: salvar dados em arquivos ou bancos de dados em formato JSON.
- Kotlin Multiplatform: a unica biblioteca de serializacao que funciona em todas as plataformas.
- Performance critica: geracao de codigo em tempo de compilacao e mais rapida que reflexao.
- Comunicacao entre servicos: usar Protobuf ou CBOR para formatos binarios eficientes.
Erros comuns
Esquecer a anotacao @Serializable: sem ela, o plugin nao gera o serializer e voce recebe um erro em tempo de execucao.
Nao aplicar o plugin do compilador: apenas adicionar a dependencia nao basta. O plugin
kotlin("plugin.serialization")deve ser aplicado no build.gradle.kts.Ignorar campos desconhecidos sem configurar: por padrao, campos extras no JSON causam excecao. Use
ignoreUnknownKeys = truequando consumir APIs externas.Confundir com Gson ou Jackson: kotlinx.serialization tem API e anotacoes proprias. Misturar anotacoes de Gson com kotlinx.serialization nao funciona.
Nao tratar nulls e ausentes: em JSON, um campo pode estar ausente ou ter valor
null. ConfigureexplicitNullsecoerceInputValuesconforme necessario.
Termos relacionados
- JSON: formato de dados textual amplamente usado em APIs web.
- Data Class: tipo ideal para serializacao por ter propriedades claras e metodo
copy. - Sealed Class: permite serializacao polimorfica com discriminador de tipo.
- KSP: processamento de simbolos, alternativa ao plugin de compilador para geracao de codigo.
- Kotlin Multiplatform: plataforma onde kotlinx.serialization brilha por funcionar em todos os targets.
- Gradle Plugin: o plugin de serializacao e aplicado no sistema de build.
kotlinx.serialization e a forma padrao e recomendada de serializar dados em Kotlin. Sua geracao de codigo em tempo de compilacao, suporte multiplataforma e integracao com o ecossistema Kotlin fazem dela a escolha natural para qualquer projeto moderno.