Getting Started

Add the dependency, create a codec, serialize your first object.

1. Add the dependency

In your build.gradle.kts:

dependencies {
    implementation("io.peekandpoke.ultra:slumber:0.107.2")
}

Slumber requires Kotlin reflection (included transitively) and runs on the JVM.

2. Create a codec

import io.peekandpoke.ultra.slumber.Codec

// The default codec handles: primitives, data classes, enums, collections,
// sealed classes, java.time.*, kotlinx.datetime.*, and MpDateTime types.
val codec = Codec.default

3. Serialize (slumber)

data class Address(val city: String, val zip: String)
data class Person(val name: String, val age: Int, val address: Address)

val alice = Person("Alice", 30, Address("Berlin", "10115"))

val data = codec.slumber(alice)
// → {
//   "name": "Alice",
//   "age": 30,
//   "address": { "city": "Berlin", "zip": "10115" }
// }

The result is a Map<String, Any?> — not a JSON string. You can pass it to any storage backend, JSON encoder, or database driver.

4. Deserialize (awake)

import io.peekandpoke.ultra.slumber.awake

val input = mapOf(
    "name" to "Bob",
    "age" to 25,
    "address" to mapOf("city" to "Munich", "zip" to "80331"),
)

val person = codec.awake<Person>(input)
// → Person(name="Bob", age=25, address=Address(city="Munich", zip="80331"))

5. Round-trip

val original = Person("Alice", 30, Address("Berlin", "10115"))
val restored = codec.awake<Person>(codec.slumber(original))

assert(restored == original) // true

How it handles Kotlin features

Nullable fields

data class Profile(val name: String, val bio: String?)

// Missing nullable field → null
codec.awake<Profile>(mapOf("name" to "Alice"))
// → Profile(name="Alice", bio=null)

// Explicit null → null
codec.awake<Profile>(mapOf("name" to "Alice", "bio" to null))
// → Profile(name="Alice", bio=null)

// Invalid data on nullable field → null (graceful degradation)
codec.awake<Profile>(mapOf("name" to "Alice", "bio" to listOf(1, 2, 3)))
// → Profile(name="Alice", bio=null)

Default parameters

data class Config(
    val host: String = "localhost",
    val port: Int = 8080,
    val debug: Boolean = false,
)

// Missing fields use defaults
codec.awake<Config>(mapOf("debug" to true))
// → Config(host="localhost", port=8080, debug=true)

// Empty map → all defaults
codec.awake<Config>(emptyMap<String, Any>())
// → Config(host="localhost", port=8080, debug=false)

Type coercion

data class Stats(val count: Int, val label: String)

// String "42" → Int 42, Number 100L → Int 100
codec.awake<Stats>(mapOf("count" to "42", "label" to 100))
// → Stats(count=42, label="100")

// Works for all numeric types: Int, Long, Double, Float, Short, Byte