Kotlin: Serialização Kotlin Explicada

Última atualização: 2026-08-26

kotlinx.serialization gera serializadores em tempo de compilação via um plugin de compilador — a conversão de Pedido ↔ JSON do Charlie é sem reflexão, segura em tipos e mais rápida que Jackson. @SerialName trata do mapeamento de campos em uma linha; Json { ignoreUnknownKeys = true } configura tolerância a falhas em uma linha.

1. O que Você Aprenderá


2. A História Real de um Desenvolvedor

(1) Problema: Bombas em Tempo de Execução na Serialização Baseada em Reflexão

Bob usava o modo de reflexão do Jackson para serializar objetos Pedido. Durante uma refatoração, orderId foi renomeado para id — a desserialização JSON falhou silenciosamente (incompatibilidade de nome de campo), e 500 registros de pedidos foram perdidos.

(2) Solução de Serialização em Tempo de Compilação

KOTLIN
// Jackson: baseado em reflexão, erros em tempo de execução
@JsonAlias("order_id")  // Fácil de esquecer
data class Order(val orderId: String, ...)

// kotlinx.serialization: tempo de compilação, erros em tempo de compilação
@Serializable
data class Order(
    @SerialName("order_id") val id: String,  // Compilador verifica!
    val total: Double
)

Geração de serializadores em tempo de compilação — mudanças de nome de campo se tornam erros de compilação, não perda de dados em tempo de execução.


3. @Serializable e o Plugin de Compilador

(1) Configuração Gradle

KOTLIN
// build.gradle.kts
plugins {
    kotlin("jvm") version "1.9.22"
    kotlin("plugin.serialization") version "1.9.22"  // Plugin de serialização do compilador
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}

(2) Serialização Básica

KOTLIN
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json

@Serializable
data class Order(val id: String, val total: Double, val status: String)

// Serializar: Objeto -> String JSON
val order = Order("ORD-001", 299.99, "CONFIRMED")
val json = Json.encodeToString(order)
// {"id":"ORD-001","total":299.99,"status":"CONFIRMED"}

// Desserializar: String JSON -> Objeto
val decoded = Json.decodeFromString<Order>(json)
// Order(id=ORD-001, total=299.99, status=CONFIRMED)

(3) Reflexão vs Serialização em Tempo de Compilação

Dimensão Jackson (Reflexão) kotlinx.serialization
Mecanismo Reflexão em tempo de execução Geração de código em tempo de compilação
Segurança Erros em tempo de execução Erros em tempo de compilação
Performance Mais lento Mais rápido (sem overhead de reflexão)
ProGuard Requer regras keep Não necessário
Multiplataforma Somente JVM JVM / Native / JS

4. Codificação e Decodificação JSON

(1) Configuração Json

KOTLIN
// Padrão: modo estrito
val strictJson = Json  // Falha em chaves desconhecidas

// Tolerante: ignora chaves desconhecidas (versionamento de API)
val lenientJson = Json {
    ignoreUnknownKeys = true        // Ignora campos não presentes na classe
    isLenient = true                // Aceita JSON malformado
    encodeDefaults = true           // Inclui campos com valores padrão
    prettyPrint = true              // Impressão formatada
    prettyPrintIndent = "  "        // Indentação
    coerceInputValues = true        // Usa padrão para null em campo não-nulo
}

val json = lenientJson.encodeToString(order)

(2) Opções de Configuração Json

Opção Padrão Descrição
ignoreUnknownKeys false Ignora campos JSON não presentes na classe
isLenient false Análise tolerante (aceita JSON não padrão)
encodeDefaults false Codifica campos com valores padrão
prettyPrint false Saída formatada
coerceInputValues false Usa valor padrão quando campo não-nulo recebe null

5. Mapeamento de Campos e Padrões

(1) Mapeamento de Campos com @SerialName

KOTLIN
@Serializable
data class Order(
    @SerialName("order_id") val id: String,       // JSON: order_id
    @SerialName("order_total") val total: Double,  // JSON: order_total
    val status: String = "PENDING"                 // JSON: status (mesmo nome)
)

// JSON: {"order_id":"ORD-001","order_total":299.99,"status":"CONFIRMED"}

(2) Campos Opcionais e Padrões

KOTLIN
@Serializable
data class Order(
    @SerialName("order_id") val id: String,
    @SerialName("order_total") val total: Double,
    val status: String = "PENDING",               // Opcional com padrão
    val customer: String? = null,                  // Opcional anulável
    @SerialName("tax_rate") val taxRate: Double = 0.08
)

// Campos opcionais ausentes usam padrões
val json = """{"order_id":"ORD-001","order_total":299.99}"""
val order = Json.decodeFromString<Order>(json)
// Order(id=ORD-001, total=299.99, status=PENDING, customer=null, taxRate=0.08)

(3) @Required para Campos Obrigatórios

KOTLIN
@Serializable
data class Order(
    @Required val id: String,       // DEVE estar presente no JSON
    @Required val total: Double     // DEVE estar presente no JSON
)
// Ausência de 'id' ou 'total' -> SerializationException

6. Suporte a Múltiplos Formatos

KOTLIN
// ProtoBuf
import kotlinx.serialization.protobuf.ProtoBuf
val protoBytes = ProtoBuf.encodeToByteArray(order)
val fromProto = ProtoBuf.decodeFromByteArray<Order>(protoBytes)

// CBOR
import kotlinx.serialization.cbor.Cbor
val cborBytes = Cbor.encodeToByteArray(order)

// HOCON (formato de configuração)
import kotlinx.serialization.hocon.Hocon

(1) Comparação de Formatos

Formato Legibilidade Tamanho Velocidade Caso de Uso
JSON Alta Grande Média Comunicação de API
ProtoBuf Baixa (binário) Pequeno Rápida RPC de alta performance
CBOR Baixa (binário) Médio Rápida IoT / embarcados
HOCON Alta Médio Média Arquivos de configuração

7. Fluxo de Codificação/Decodificação da Serialização

100%
sequenceDiagram
    participant Obj as Objeto Order
    participant Ser as Serializador
    participant JSON as String JSON

    Note over Obj,Ser: Codificação
    Obj->>Ser: propriedades @Serializable
    Ser->>JSON: encodeToString()
    Note over JSON: {"order_id":"ORD-001",...}

    Note over Ser,Obj: Decodificação
    JSON->>Ser: decodeFromString<Order>(json)
    Ser->>Obj: construtor @Serializable
    Note over Obj: Order(id=ORD-001,...)

8. Exemplo Completo: Serialização JSON do OrderProcessor

KOTLIN
// ============================================
// OrderProcessor - Serialização JSON
// Funcionalidade: Pedido <-> JSON com mapeamento de campos
// ============================================

import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class Address(
    val street: String,
    val city: String,
    val country: String
)

@Serializable
data class OrderItem(
    val sku: String,
    val quantity: Int,
    @SerialName("unit_price") val unitPrice: Double
) {
    val subtotal: Double get() = quantity * unitPrice
}

@Serializable
data class Order(
    @SerialName("order_id") val id: String,
    @SerialName("order_total") val total: Double,
    val status: String = "PENDING",
    val customer: String? = null,
    val items: List<OrderItem> = emptyList(),
    val address: Address? = null,
    @SerialName("tax_rate") val taxRate: Double = 0.08,
    @SerialName("created_at") val createdAt: String = "2026-01-01T00:00:00Z"
)

val orderJson = Json {
    ignoreUnknownKeys = true
    encodeDefaults = true
    prettyPrint = true
    prettyPrintIndent = "  "
}

fun main() {
    // Criar pedido
    val order = Order(
        id = "ORD-001",
        total = 299.99,
        status = "CONFIRMED",
        customer = "Alice",
        items = listOf(
            OrderItem("SKU-WIDGET", 3, 9.99),
            OrderItem("SKU-GADGET", 1, 149.99)
        ),
        address = Address("123 Main St", "New York", "US"),
        createdAt = "2026-07-13T10:30:00Z"
    )

    // Serializar: Pedido -> JSON
    println("=== Serializar ===")
    val jsonString = orderJson.encodeToString(order)
    println(jsonString)

    // Desserializar: JSON -> Pedido
    println("\n=== Desserializar ===")
    val decoded = orderJson.decodeFromString<Order>(jsonString)
    println("Pedido: ${decoded.id}, Total: \$${decoded.total} USD")
    println("Itens: ${decoded.items.map { "${it.sku} x${it.quantity}" }}")

    // Tratar chaves desconhecidas (versionamento de API)
    println("\n=== Versionamento de API ===")
    val jsonWithExtraFields = """
        {
          "order_id": "ORD-002",
          "order_total": 1500.00,
          "status": "SHIPPED",
          "unknown_field": "isso é ok",
          "new_api_version": 2
        }
    """.trimIndent()
    val decodedWithExtra = orderJson.decodeFromString<Order>(jsonWithExtraFields)
    println("Desserializado com campos extras: ${decodedWithExtra.id}")

    // JSON mínimo (apenas campos obrigatórios)
    println("\n=== JSON Mínimo ===")
    val minimalJson = """{"order_id":"ORD-003","order_total":45.50}"""
    val minimal = orderJson.decodeFromString<Order>(minimalJson)
    println("Mínimo: ${minimal.id}, Status: ${minimal.status}, Cliente: ${minimal.customer}")
}

Saída:

TEXT 📖 Somente leitura
=== Serializar ===
{
  "order_id": "ORD-001",
  "order_total": 299.99,
  "status": "CONFIRMED",
  "customer": "Alice",
  "items": [
    {
      "sku": "SKU-WIDGET",
      "quantity": 3,
      "unit_price": 9.99
    },
    {
      "sku": "SKU-GADGET",
      "quantity": 1,
      "unit_price": 149.99
    }
  ],
  "address": {
    "street": "123 Main St",
    "city": "New York",
    "country": "US"
  },
  "tax_rate": 0.08,
  "created_at": "2026-07-13T10:30:00Z"
}

=== Desserializar ===
Pedido: ORD-001, Total: $299.99 USD
Itens: [SKU-WIDGET x3, SKU-GADGET x1]

=== Versionamento de API ===
Desserializado com campos extras: ORD-002

=== JSON Mínimo ===
Mínimo: ORD-003, Status: PENDING, Cliente: null

9. Exemplos práticos rápidos

▶ Exemplo: Serialização básica com @Serializable

KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class Product(val sku: String, val name: String, val price: Double)

val product = Product("SKU-001", "Caneta", 2.99)

// Para serializar
val json = Json.encodeToString(product)
println("JSON: $json")

// Para deserializar
val parsed = Json.decodeFromString<Product>(json)
println("Parsed: $parsed (price=${parsed.price})")

// Pretty printing
val pretty = Json { prettyPrint = true }
println("Pretty: ${pretty.encodeToString(product)}")

// Saída:
// JSON: {"sku":"SKU-001","name":"Caneta","price":2.99}
// Parsed: Product(sku=SKU-001, name=Caneta, price=2.99)
// Pretty: {
//     "sku": "SKU-001",
//     "name": "Caneta",
//     "price": 2.99
// }

▶ Exemplo: Campos opcionais com default

KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class Config(
    val env: String = "development",
    val timeout: Int = 30,
    val retries: Int = 3
)

// Serialização completa
val json = Json.encodeToString(Config("production", 60, 5))
println(json)

// Deserialização com campos ausentes usa defaults
val partial = """{"env": "staging"}"""
val config = Json.decodeFromString<Config>(partial)
println("Config: $config")

// Saída:
// {"env":"production","timeout":60,"retries":5}
// Config: Config(env=staging, timeout=30, retries=3)

▶ Exemplo: Valores null e campos opcionais

KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class Customer(
    val id: String,
    val name: String,
    val email: String? = null,
    val phone: String? = null
)

val customer = Customer("CUST-001", "Alice", email = "alice@example.com")
val json = Json.encodeToString(customer)
println(json)

// Decodificar JSON com campos nulos
val parsed = Json.decodeFromString<Customer>(
    """{"id": "CUST-002", "name": "Bob"}"""
)
println(parsed)
println("Email: ${parsed.email ?: "(não informado)"}")

// Saída:
// {"id":"CUST-001","name":"Alice","email":"alice@example.com","phone":null}
// Customer(id=CUST-002, name=Bob, email=null, phone=null)
// Email: (não informado)

▶ Exemplo: Enums e sealed classes

KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
enum class Status { PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED }

@Serializable
data class Order(
    val id: String,
    val status: Status
)

val order = Order("ORD-001", Status.CONFIRMED)
val json = Json.encodeToString(order)
println(json)

// Decodificar - enums case-sensitive por padrão
val parsed = Json.decodeFromString<Order>(json)
println("Status: ${parsed.status}")

// Saída:
// {"id":"ORD-001","status":"CONFIRMED"}
// Status: CONFIRMED

▶ Exemplo: Serializers customizados

KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
import java.util.Date

// Custom serializer para Date (epoch millis como Long)
object DateAsEpochSerializer : KSerializer<Date> {
    override val descriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
    override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
    override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
}

@Serializable
data class Event(
    val id: String,
    @Serializable(with = DateAsEpochSerializer::class) val timestamp: Date
)

val event = Event("EVT-001", Date())
val json = Json.encodeToString(event)
println("Serialized: $json")

val parsed = Json.decodeFromString<Event>(json)
println("Parsed: $parsed")

// Saída:
// Serialized: {"id":"EVT-001","timestamp":1700000000000}
// Parsed: Event(id=EVT-001, timestamp=...)

▶ Exemplo: Coleções aninhadas

KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class Cart(val customer: String, val items: List<Item>)

@Serializable
data class Item(val sku: String, val quantity: Int, val unitPrice: Double)

val cart = Cart(
    customer = "Alice",
    items = listOf(
        Item("SKU-001", 2, 29.99),
        Item("SKU-002", 1, 149.99)
    )
)

val json = Json { prettyPrint = true }.encodeToString(cart)
println(json)

// Saída:
// {
//     "customer": "Alice",
//     "items": [
//         { "sku": "SKU-001", "quantity": 2, "unitPrice": 29.99 },
//         { "sku": "SKU-002", "quantity": 1, "unitPrice": 149.99 }
//     ]
// }

▶ Exemplo: Polimorfismo e serialName

KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.PolymorphicSerializer

@Serializable
sealed class ApiResponse<out T> {
    @Serializable
    @SerialName("success")
    data class Success<T>(val data: T) : ApiResponse<T>()

    @Serializable
    @SerialName("error")
    data class Error(val message: String) : ApiResponse<Nothing>()
}

// Encoded com discriminador
val success: ApiResponse<Order> = ApiResponse.Success(Order("ORD-001", Status.PENDING))
val err: ApiResponse<Order> = ApiResponse.Error("Falha de rede")

println(Json.encodeToString(success))
println(Json.encodeToString(err))

// Saída:
// {"type":"success","data":{"id":"ORD-001","status":"PENDING"}}
// {"type":"error","message":"Falha de rede"}

❓ Perguntas Frequentes

P: O kotlinx.serialization pode ser usado junto com o Jackson? R: Sim, mas não é recomendado. Os dois mecanismos de serialização são diferentes. Durante a migração, você pode usar @JsonAlias como ponte; para novos projetos, use kotlinx.serialization diretamente.

P: Sealed classes podem ser serializadas? R: Sim. Sealed classes @Serializable incluem automaticamente um discriminador de tipo ("type":"SubClassName"), e a subclasse correta é escolhida durante a desserialização.

P: Como personalizar a lógica de serialização? R: Especifique um serializador personalizado com @Serializable(with = CustomSerializer::class), ou implemente a interface KSerializer<T> para controlar manualmente a codificação/decodificação.

P: A serialização suporta genéricos? R: Sim, mas você precisa anotar a classe genérica com @Serializable. decodeFromString requer parâmetros de tipo explícitos.

P: Por que o plugin do compilador é necessário? R: O plugin do compilador Kotlin gera automaticamente código de serializador para classes @Serializable em tempo de compilação, evitando reflexão em tempo de execução. Esta é a base de sua performance e segurança.

P: Qual a diferença entre @SerialName e @JsonProperty? R: Funcionalmente iguais (mapeamento de nome de campo JSON), mas @SerialName é a anotação do kotlinx.serialization processada em tempo de compilação; @JsonProperty é a anotação do Jackson processada em tempo de execução via reflexão.


📖 Resumo


📝 Exercícios

  1. Iniciante (⭐): Adicione @Serializable a Order, serialize para JSON e desserialize de volta. Dica: @Serializable data class Order(...)
  2. Intermediário (⭐⭐): Use @SerialName para mapear nomes de campos de Order (order_id, order_total) e configure Json { ignoreUnknownKeys = true } para evolução de API. Dica: Consulte a Seção 5
  3. Desafio (⭐⭐⭐): Implemente um serializador personalizado que achate os itens de Order em um formato JSON plano (expandindo itens para o formato item_1_sku, item_1_qty). Dica: Implemente KSerializer<Order>

← Anterior | Próximo →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%