
Byte-first, compile-time JSON serializer generating zero-allocation codecs with bitwise O(1) trie field matching, required-field bitmask checks, dedicated readers and thread-local pools for high-throughput, low-memory endpoints.
Keep kotlinx.serialization (or Moshi / Gson / Jackson). Add one annotation on the DTO that hurts — same Ktor, Retrofit, or Spring stack.
Ghost is not a rewrite. It is the fast, low-allocation path for the models you opt in — and a softer landing when the backend ships messy JSON.
Quick Start — first DTO in minutes → · Try it in the browser → · Maven Central · Roadmap
Strict JSON fails the whole request. Ghost can keep going where you opt in:
{ "id": "u1", "name": "Ada", "age": { "years": 36 } }@Serializable
@GhostSerialization
data class User(
val id: String,
val name: String,
@GhostResilient val age: Int = 0, // object instead of number → 0, parse continues
)Unknown sealed variants → @GhostFallback. Opaque blobs → RawJson. Decode errors include a JSONPath (e.g. $.user.age) and, when useful, a fix hint in GhostJsonException / GhostYamlException (JSON, Proto3 JSON, YAML cursor). Details → Advanced Features
Ghost generates only for @GhostSerialization — not for every @Serializable in the module.
@Serializable // keep — KotlinX call sites still work
@GhostSerialization // add — Ghost codegen for this class only
data class User(
@SerialName("user_id") // keep — Ghost uses this as the wire name
val id: Long,
val name: String,
)
Ghost.deserialize<User>(responseBytes) // Ghost (bytes / adapters)
// Json.decodeFromString<User>(...) // still fine on KotlinX@GhostName — optional if @SerialName is already set (GhostName wins only if both differ).@GhostResilient / @GhostFallback / RawJson — Ghost-only; omit if you don’t need them.@GhostSerialization too.| Drop-in | |
|---|---|
| Ktor |
ghost() next to json() · or bodyGhost / respondGhost
|
| Retrofit |
GhostConverterFactory before Gson / Moshi / KotlinX |
| Spring |
ghost-spring-boot-starter — Ghost types via Ghost; Jackson for the rest |
Guides: Quick Start · Ktor · Android / Retrofit · Spring
Also: Android · iOS · JVM · Wasm · YAML · Proto3 JSON · Kotlin 2.4.0 / KSP 2.3.10 / Ktor 3.5.x → Modules
On hot models Ghost is routinely several× faster and far leaner than KotlinX / Moshi. Most APIs are small — use it on the hotspot, not everywhere.
Twitter macro (631 KB) decode:
| Ghost | KSER | Moshi | |
|---|---|---|---|
| String | 1.2 GB/s · 361 KB | 0.72 GB/s · 1338 KB | 0.37 GB/s · 1709 KB |
| Bytes | 1.05 GB/s · 621 KB | 0.42 GB/s · 4297 KB | 0.28 GB/s · 4668 KB |
| Streaming | 0.53 GB/s · 1269 KB | 0.19 GB/s · 1905 KB | 0.43 GB/s · 1709 KB |
Benchmarks · HTTP Arena · Playground Speed Test
Run it yourself: ./gradlew :ghost-serialization:yamlComplianceMatrix · Details → YAML Conformance
Quick Start · Installation · Android · KMP / Ktor · iOS · Spring · YAML · Proto3 JSON · Advanced · Architecture · Contributing · Support & Versioning
Keep kotlinx.serialization (or Moshi / Gson / Jackson). Add one annotation on the DTO that hurts — same Ktor, Retrofit, or Spring stack.
Ghost is not a rewrite. It is the fast, low-allocation path for the models you opt in — and a softer landing when the backend ships messy JSON.
Quick Start — first DTO in minutes → · Try it in the browser → · Maven Central · Roadmap
Strict JSON fails the whole request. Ghost can keep going where you opt in:
{ "id": "u1", "name": "Ada", "age": { "years": 36 } }@Serializable
@GhostSerialization
data class User(
val id: String,
val name: String,
@GhostResilient val age: Int = 0, // object instead of number → 0, parse continues
)Unknown sealed variants → @GhostFallback. Opaque blobs → RawJson. Decode errors include a JSONPath (e.g. $.user.age) and, when useful, a fix hint in GhostJsonException / GhostYamlException (JSON, Proto3 JSON, YAML cursor). Details → Advanced Features
Ghost generates only for @GhostSerialization — not for every @Serializable in the module.
@Serializable // keep — KotlinX call sites still work
@GhostSerialization // add — Ghost codegen for this class only
data class User(
@SerialName("user_id") // keep — Ghost uses this as the wire name
val id: Long,
val name: String,
)
Ghost.deserialize<User>(responseBytes) // Ghost (bytes / adapters)
// Json.decodeFromString<User>(...) // still fine on KotlinX@GhostName — optional if @SerialName is already set (GhostName wins only if both differ).@GhostResilient / @GhostFallback / RawJson — Ghost-only; omit if you don’t need them.@GhostSerialization too.| Drop-in | |
|---|---|
| Ktor |
ghost() next to json() · or bodyGhost / respondGhost
|
| Retrofit |
GhostConverterFactory before Gson / Moshi / KotlinX |
| Spring |
ghost-spring-boot-starter — Ghost types via Ghost; Jackson for the rest |
Guides: Quick Start · Ktor · Android / Retrofit · Spring
Also: Android · iOS · JVM · Wasm · YAML · Proto3 JSON · Kotlin 2.4.0 / KSP 2.3.10 / Ktor 3.5.x → Modules
On hot models Ghost is routinely several× faster and far leaner than KotlinX / Moshi. Most APIs are small — use it on the hotspot, not everywhere.
Twitter macro (631 KB) decode:
| Ghost | KSER | Moshi | |
|---|---|---|---|
| String | 1.2 GB/s · 361 KB | 0.72 GB/s · 1338 KB | 0.37 GB/s · 1709 KB |
| Bytes | 1.05 GB/s · 621 KB | 0.42 GB/s · 4297 KB | 0.28 GB/s · 4668 KB |
| Streaming | 0.53 GB/s · 1269 KB | 0.19 GB/s · 1905 KB | 0.43 GB/s · 1709 KB |
Benchmarks · HTTP Arena · Playground Speed Test
Run it yourself: ./gradlew :ghost-serialization:yamlComplianceMatrix · Details → YAML Conformance
Quick Start · Installation · Android · KMP / Ktor · iOS · Spring · YAML · Proto3 JSON · Advanced · Architecture · Contributing · Support & Versioning