Kotlin Multiplatform library to support Smile format for kotlinx.serialization
.
Smile
is an efficient JSON-compatible binary data format, initially developed by Jackson JSON processor project team. Its logical data model is same as that of JSON, so it can be considered a "Binary JSON" format.
Specification for the format defined in Smile Format Specification.
Why use Smile?
Jackson maintainer Tatu Saloranta wrote an email describing different binary alternatives to JSON: https://groups.google.com/g/jackson-user/c/oQyF4Rit5lw/m/LxP33PbWn9EJ
One of the major products using Smile is ElasticSearch, that can use Smile and JSON interchangeably, and by using Smile you get both better performance and smaller stored data size: https://medium.com/its-tinkoff/elasticsearch-with-a-smile-d105f4b60d83
Performance comparison on the same data as the benchmark in this library:
Benchmark Mode Cnt Score Error Units
JacksonJsonSmileBenchmark.jsonLargeDeserialize thrpt 5 79.037 ± 2.253 ops/ms
JacksonJsonSmileBenchmark.smileLargeDeserialize thrpt 5 117.915 ± 0.301 ops/ms
JacksonJsonSmileBenchmark.jsonLargeSerialize thrpt 5 154.320 ± 0.421 ops/ms
JacksonJsonSmileBenchmark.smileLargeSerialize thrpt 5 252.946 ± 0.717 ops/ms
JacksonJsonSmileBenchmark.jsonSmallDeserialize thrpt 5 172.191 ± 11.006 ops/ms
JacksonJsonSmileBenchmark.smileSmallDeserialize thrpt 5 226.521 ± 18.704 ops/ms
JacksonJsonSmileBenchmark.jsonSmallSerialize thrpt 5 407.084 ± 3.078 ops/ms
JacksonJsonSmileBenchmark.smileSmallSerialize thrpt 5 616.633 ± 2.665 ops/ms
Library does not have any non-multiplatform dependencies, so adding any new platform should not be an issue.
Currently it is compiled for:
- jvm
- macosArm64
- iosArm64
- iosSimulatorArm64
- mingwX64
- linuxX64
- js (browser/nodejs)
- wasm (browser/nodejs)
Library is published to Maven Central under name io.github.vooft:kotlinx-serialization-smile-core.
Add the dependency to your project:
kotlin {
...
sourceSets {
commonMain.dependencies {
implementation("io.github.vooft:kotlinx-serialization-smile-core:<version>")
}
}
}
Then in your code just create serializable classes as normal and use class io.github.vooft.kotlinsmile.Smile
to encode/decode them.
@kotlinx.serialization.Serializable
data class Test(val name: String, val number: Int)
fun main() {
val obj = Test("test", 42)
// encode object to ByteArray
val encoded = Smile.encode(obj)
// decode ByteArray to object
val decoded = Smile.decode<Test>(encoded)
}
This will use the default configuration.
The goal is to support all the features defined in the Smile specification, at the moment library can encode and decode almost all messages that produced by the default Jackson ObjectMapper configuration (exceptions are noted below).
- ✅ Structural types
- ✅ Classes (incl. nested classes)
- ✅ Arrays
- ✅ Collections (List, Set)
- ✅ Maps
- ✅ Simple literals (empty string, null, boolean)
- ✅ Enums
- ❗Integers
- ✅ Small integers (values in range [-16..15])
- ✅ 32-bit integers
- ✅ 64-bit integers
- ❌ BigInteger
- ❗Floating-point numbers
- ✅ 32-bit Float
- ✅ 64-bit Double
- ❌ BigDecimal
- ✅ Strings
- ✅ Tiny/Short/Long Ascii
- ✅ Tiny/Short/Long Unicode
- ✅ Binary data (only safe 7-bit encoding)
- ✅ Shared string references
- ✅ Value short/long reference
- ✅ Key short/long reference
- ✅ Shared property names
- ✅ Shared string values
- ❌ Raw binary data (by default 7-bit encoding is used, this flag enables unescaped 8-bit encoding)
- ❌ Optional header (library always expects header and always writes one)
There is no standard BigInteger
or BigDecimal
implementation in Kotlin, potentially can use kotlin-multiplatform-bignum library.
Library is designed to be fast and efficient, a benchmark is available in the kotlinx-serialization-smile-benchmark module.
It is slightly less performant on JVM than Jackson in some cases (executed on M2 Macbook Pro):
Benchmark Mode Cnt Score Error Units
JacksonSmileBenchmark.largeDeserializeJackson thrpt 10 114.965 ± 0.221 ops/ms
JacksonSmileBenchmark.largeDeserializeKotlin thrpt 10 112.486 ± 0.424 ops/ms
JacksonSmileBenchmark.largeSerializeJackson thrpt 10 234.643 ± 4.723 ops/ms
JacksonSmileBenchmark.largeSerializeKotlin thrpt 10 163.857 ± 2.020 ops/ms
JacksonSmileBenchmark.smallDeserializeJackson thrpt 10 229.096 ± 2.384 ops/ms
JacksonSmileBenchmark.smallDeserializeKotlin thrpt 10 282.952 ± 1.666 ops/ms
JacksonSmileBenchmark.smallSerializeJackson thrpt 10 589.985 ± 3.787 ops/ms
JacksonSmileBenchmark.smallSerializeKotlin thrpt 10 459.816 ± 1.810 ops/ms