Use Moshi with "lite" Reflection
This recipe will teach you how to replace Moshi's default kotlin-reflect
backend with the lightweight kotlinx-metadata
variant.
By default, the moshi-kotlin
module uses kotlin-reflect
, which is perfectly fine for most uses.
However, at 3 MB, the relatively large JAR size of kotlin-reflect
can have a meaningful performance impact in some scenarios:
such as AWS Lambda cold-starts.
Moshi is likely to adopt kotlinx-metadata
in the future, as you can see in this approved pull request.
But until then, you can use this recipe to take advantage of the performance gains.
Gradle setup¶
This recipe uses the 3rd-party moshi-metadata-reflect module.
dependencies {
implementation(platform("org.http4k:http4k-bom:5.10.4.0"))
implementation("org.http4k:http4k-format-moshi") {
exclude("com.squareup.moshi", "moshi-kotlin")
}
implementation("dev.zacsweers.moshix:moshi-metadata-reflect:0.19.0")
}
If you wish to take full advantage of the performance benefits, you need to ensure kotlin-reflect
isn't bundled into your final jar.
You must:
- Exclude the original
moshi-kotlin
module fromhttp4k-format-moshi
- Ensure you don't have other 3rd-party libraries that depend on
kotlin-reflect
Recipe¶
The first step is to define a "lite" version of ConfigurableMoshi
.
This is done by overriding the default kotlin-reflect
backend with the kotlinx-metadata
backend, via the 3rd-party moshi-metadata-reflect module.
package guide.howto.moshi_lite
import com.squareup.moshi.Moshi
import dev.zacsweers.moshix.reflect.MetadataKotlinJsonAdapterFactory
import org.http4k.format.ConfigurableMoshi
import org.http4k.format.EventAdapter
import org.http4k.format.ListAdapter
import org.http4k.format.MapAdapter
import org.http4k.format.ThrowableAdapter
import org.http4k.format.asConfigurable
import org.http4k.format.withStandardMappings
object MoshiLite: ConfigurableMoshi(
Moshi.Builder()
.addLast(EventAdapter)
.addLast(ThrowableAdapter)
.addLast(ListAdapter)
.addLast(MapAdapter)
.asConfigurable(MetadataKotlinJsonAdapterFactory()) // <-- moshi-metadata-reflect
.withStandardMappings()
.done()
)
Then you can use this new ConfigurableMoshi
instance for auto-marshalling and lens creation, like normal.
package guide.howto.moshi_lite
import org.http4k.core.Method.GET
import org.http4k.core.Request
import org.http4k.core.with
import java.util.UUID
data class MoshiCat(val id: UUID, val name: String)
fun main() {
val cat = MoshiCat(UUID.randomUUID(), "Tigger")
// serialize
val string = MoshiLite.asFormatString(cat)
.also(::println)
// deserialize
MoshiLite.asA<MoshiCat>(string)
.also(::println)
// make a lens
val lens = MoshiLite.autoBody<MoshiCat>().toLens()
Request(GET, "foo").with(lens of cat)
}