mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
perf: drop jackson-module-kotlin, removing kotlin-reflect from the app
Nothing in the app binds JSON reflectively any more, so the module whose whole
job is reflective Kotlin binding has no work left to do. Swapping it for plain
jackson-databind takes kotlin-reflect out with it:
kotlin.reflect.jvm.internal 969 -> 0 classes
com.fasterxml.jackson.module.kotlin 73 -> 0 classes
DEX 30.94 MB -> 29.94 MB, 3 files -> 2
A megabyte and a whole secondary DEX, for code the app never called.
`jacksonObjectMapper()` became `ObjectMapper()` in all eight remaining places:
JacksonMapper and JsonMapperNip55 (both drive registered StdSerializer /
StdDeserializer pairs) and six readTree-only users (LNURL resolvers, TorService,
ArtiGuardState, Nip96Uploader). None of them needed the Kotlin module.
The one thing the module did that databind has no equivalent for is
`jacksonTypeRef<T>()`, which NIP-55 needs: `readValue<List<IntentResult>>` erases
to `List` under `T::class.java` and every element comes back a LinkedHashMap. It
is a one-liner — `reified` substitutes the concrete type into the anonymous
subclass before erasure — so it now lives in quartz as jacksonTypeRefOf().
quartz exported the module as an `api` dependency, so test sources three modules
away were quietly using it. Those move over too, same two swaps: `ObjectMapper()`
where the mapper only walks maps or readTree (the pretty-printer test, the BIP-39
vectors, the event-command benchmark, quic's qlog writer and its test), and
`jacksonTypeRefOf<ArrayList<Event>>()` where the reified `readValue<T>` extension
was doing the typeref's job (LargeDBSignatureCheck, the thread-order test, the
two cache benchmarks). Event's own deserializer is registered on the mapper, so
databind resolves it either way.
`cli` keeps the module. It genuinely binds its config files reflectively
(Config, OperatorKeys, Aliases, StatusReport), it declares the dependency itself,
and it is never minified, so none of this applies there. desktopApp, geode,
relayBench and quic-interop likewise declare it directly and are unaffected.
Verified: `./gradlew test` green, the instrumented sources compile
(:quartz:compileAndroidDeviceTestSources, :amethyst and :benchmark androidTest),
R8 release build green, 19/19 contract checks, and both package prefixes
confirmed at zero classes in the shipped DEX.
Note for whoever hits it next: a full recompile of :amethyst needs more than a
2 GB Kotlin daemon. Mine died with "Couldn't transform method node" pointing at a
Compose lambda in NoteCompose.kt, which is an OutOfMemoryError in the ASM frame
analyzer wearing a disguise, not a code fault.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEoxktEZyTrAS33vVZBiwm
This commit is contained in:
+2
-2
@@ -21,7 +21,6 @@
|
||||
package com.vitorpamplona.amethyst
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.assemblers.CashuMintDirectoryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nip47WalletConnect.NWCPaymentFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.thread.ThreadFeedFilter
|
||||
@@ -34,6 +33,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.verify
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.JacksonMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.jacksonTypeRefOf
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.aTag.ATag
|
||||
@@ -163,7 +163,7 @@ class ThreadDualAxisChartAssemblerTest {
|
||||
fun threadOrderTest() =
|
||||
runBlocking {
|
||||
val eventArray =
|
||||
JacksonMapper.mapper.readValue<List<Event>>(db) + Event.fromJson(header)
|
||||
JacksonMapper.mapper.readValue(db, jacksonTypeRefOf<List<Event>>()) + Event.fromJson(header)
|
||||
|
||||
var counter = 0
|
||||
eventArray.forEach {
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.service.lnurl
|
||||
|
||||
import android.content.Context
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.HttpStatusMessages
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@@ -167,7 +167,7 @@ class LightningAddressResolver {
|
||||
|
||||
val errorMessage =
|
||||
runCatching {
|
||||
jacksonObjectMapper().readTree(body)
|
||||
ObjectMapper().readTree(body)
|
||||
}.getOrNull()?.let { tree ->
|
||||
val errorNode = tree.get("error")
|
||||
val messageNode = tree.get("message")
|
||||
@@ -218,7 +218,7 @@ class LightningAddressResolver {
|
||||
context: Context,
|
||||
onZapRequestSent: (LnZapRequestEvent?) -> Unit = {},
|
||||
): String {
|
||||
val mapper = jacksonObjectMapper()
|
||||
val mapper = ObjectMapper()
|
||||
|
||||
val lnurlpUrl = assembleUrl(lnAddress)
|
||||
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.core.net.toFile
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.HttpStatusMessages
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
@@ -209,7 +209,7 @@ class Nip96Uploader {
|
||||
|
||||
val errorMessage =
|
||||
try {
|
||||
val tree = jacksonObjectMapper().readTree(msg)
|
||||
val tree = ObjectMapper().readTree(msg)
|
||||
val status = tree.get("status")?.asText()
|
||||
val message = tree.get("message")?.asText()
|
||||
if (status == "error" && message != null) {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.tor
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
|
||||
/**
|
||||
* Pure, file- and JNI-free parsers over Arti's persisted guard sample
|
||||
@@ -42,7 +42,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
* circuit — i.e. a real bootstrap reached the guard-confirmation stage.
|
||||
*/
|
||||
object ArtiGuardState {
|
||||
private val mapper = jacksonObjectMapper()
|
||||
private val mapper = ObjectMapper()
|
||||
|
||||
/** Convenience for tests/callers holding the raw file text. */
|
||||
fun parse(json: String): JsonNode = mapper.readTree(json)
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.tor
|
||||
|
||||
import android.content.Context
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -206,7 +206,7 @@ class TorService(
|
||||
val file = guardsFile()
|
||||
if (!file.exists()) return null
|
||||
return try {
|
||||
jacksonObjectMapper().readTree(file)
|
||||
ObjectMapper().readTree(file)
|
||||
} catch (e: Exception) {
|
||||
Log.w("TorService") { "Could not inspect guards.json: ${e.message}" }
|
||||
null
|
||||
|
||||
+3
-2
@@ -20,10 +20,10 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.benchmark
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.JacksonMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.jacksonTypeRefOf
|
||||
import com.vitorpamplona.quartz.utils.cache.LargeCache
|
||||
import org.junit.Assert.assertTrue
|
||||
import java.util.function.Consumer
|
||||
@@ -35,8 +35,9 @@ open class BaseLargeCacheBenchmark {
|
||||
// This file includes duplicates
|
||||
val fullDBInputStream = javaClass.classLoader!!.getResourceAsStream("nostr_vitor_startup_data.json.gz")
|
||||
|
||||
return JacksonMapper.mapper.readValue<ArrayList<Event>>(
|
||||
return JacksonMapper.mapper.readValue(
|
||||
GZIPInputStream(fullDBInputStream),
|
||||
jacksonTypeRefOf<ArrayList<Event>>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ package com.vitorpamplona.quartz.benchmark
|
||||
import androidx.benchmark.junit4.BenchmarkRule
|
||||
import androidx.benchmark.junit4.measureRepeated
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.JacksonMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.jacksonTypeRefOf
|
||||
import com.vitorpamplona.quartz.utils.cache.LargeCache
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Rule
|
||||
@@ -43,8 +43,9 @@ open class BaseCacheBenchmark {
|
||||
// This file includes duplicates
|
||||
val fullDBInputStream = javaClass.classLoader?.getResourceAsStream("nostr_vitor_startup_data.json.gz")
|
||||
|
||||
return JacksonMapper.mapper.readValue<ArrayList<Event>>(
|
||||
return JacksonMapper.mapper.readValue(
|
||||
GZIPInputStream(fullDBInputStream),
|
||||
jacksonTypeRefOf<ArrayList<Event>>(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.benchmark
|
||||
import androidx.benchmark.junit4.BenchmarkRule
|
||||
import androidx.benchmark.junit4.measureRepeated
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.utils.sha256.Sha256Hasher
|
||||
import org.junit.Rule
|
||||
@@ -54,7 +54,7 @@ class EventCmdSerializerBenchmark {
|
||||
|
||||
@Test
|
||||
fun jsonStringEncoderJackson() {
|
||||
val jsonMapper = jacksonObjectMapper()
|
||||
val jsonMapper = ObjectMapper()
|
||||
benchmarkRule.measureRepeated {
|
||||
jsonMapper.writeValueAsString(specialEncoders)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class EventCmdSerializerBenchmark {
|
||||
|
||||
@Test
|
||||
fun jsonStringEncoderSha256Jackson() {
|
||||
val jsonMapper = jacksonObjectMapper()
|
||||
val jsonMapper = ObjectMapper()
|
||||
benchmarkRule.measureRepeated {
|
||||
val digest = Sha256Hasher()
|
||||
digest.hash(jsonMapper.writeValueAsString(specialEncoders).toByteArray())
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.service.lnurl
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.vitorpamplona.quartz.lightning.LnInvoiceUtil
|
||||
import com.vitorpamplona.quartz.lightning.Lud06
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
||||
@@ -48,7 +48,7 @@ import kotlin.coroutines.cancellation.CancellationException
|
||||
class LightningAddressResolver(
|
||||
private val httpClient: OkHttpClient,
|
||||
) {
|
||||
private val mapper = jacksonObjectMapper()
|
||||
private val mapper = ObjectMapper()
|
||||
|
||||
/**
|
||||
* Result of resolving a lightning address to a BOLT11 invoice.
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.service.lnurl
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlEndpointCache
|
||||
import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlEndpointInfo
|
||||
import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlEndpointResolver
|
||||
@@ -47,7 +47,7 @@ import kotlin.coroutines.cancellation.CancellationException
|
||||
class OkHttpLnurlEndpointResolver(
|
||||
private val okHttpClient: (String) -> OkHttpClient,
|
||||
) : LnurlEndpointResolver {
|
||||
private val mapper = jacksonObjectMapper()
|
||||
private val mapper = ObjectMapper()
|
||||
|
||||
override suspend fun resolve(lnurlpUrl: String): LnurlEndpointInfo? = LnurlEndpointCache.getOrFetch(lnurlpUrl, ::fetch)
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ firebaseBom = "34.19.0"
|
||||
fragmentKtx = "1.9.0"
|
||||
gms = "4.5.0"
|
||||
healthConnect = "1.1.0"
|
||||
jackson = "2.22.2"
|
||||
jacksonModuleKotlin = "2.22.2"
|
||||
javaKeyring = "1.0.4"
|
||||
kmpTorRuntime = "2.6.0"
|
||||
@@ -203,6 +204,7 @@ google-mlkit-genai-rewriting = { group = "com.google.mlkit", name = "genai-rewri
|
||||
google-mlkit-genai-image-description = { group = "com.google.mlkit", name = "genai-image-description", version.ref = "genaiImageDescription" }
|
||||
google-mlkit-language-id = { group = "com.google.mlkit", name = "language-id", version.ref = "languageId" }
|
||||
google-mlkit-translate = { group = "com.google.mlkit", name = "translate", version.ref = "translate" }
|
||||
jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" }
|
||||
jackson-module-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin", version.ref = "jacksonModuleKotlin" }
|
||||
java-keyring = { group = "com.github.javakeyring", name = "java-keyring", version.ref = "javaKeyring" }
|
||||
kmp-tor-runtime = { group = "io.matthewnelson.kmp-tor", name = "runtime", version.ref = "kmpTorRuntime" }
|
||||
|
||||
+10
-2
@@ -177,8 +177,16 @@ kotlin {
|
||||
dependsOn(commonMain.get())
|
||||
|
||||
dependencies {
|
||||
// Performant Parser of JSONs into Events
|
||||
api(libs.jackson.module.kotlin)
|
||||
// Performant Parser of JSONs into Events.
|
||||
//
|
||||
// jackson-databind, NOT jackson-module-kotlin: the module exists to bind
|
||||
// Kotlin classes reflectively off their constructor parameter names, and
|
||||
// nothing in the app does that any more — every wire format goes through a
|
||||
// hand-written serializer or kotlinx. Dropping it takes kotlin-reflect with
|
||||
// it, which is ~1,000 classes of DEX the app never called. `cli` still
|
||||
// declares the module itself: it genuinely binds its config files
|
||||
// reflectively, and is never minified.
|
||||
api(libs.jackson.databind)
|
||||
|
||||
// Websockets API
|
||||
implementation(libs.okhttp)
|
||||
|
||||
+5
-3
@@ -21,10 +21,10 @@
|
||||
package com.vitorpamplona.quartz
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.verify
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.JacksonMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.jacksonTypeRefOf
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertTrue
|
||||
@@ -41,8 +41,9 @@ class LargeDBSignatureCheck {
|
||||
val fullDBInputStream = javaClass.classLoader?.getResourceAsStream("nostr_vitor_short.json")
|
||||
|
||||
val eventArray =
|
||||
JacksonMapper.mapper.readValue<ArrayList<Event>>(
|
||||
JacksonMapper.mapper.readValue(
|
||||
InputStreamReader(fullDBInputStream),
|
||||
jacksonTypeRefOf<ArrayList<Event>>(),
|
||||
) as List<Event>
|
||||
|
||||
var counter = 0
|
||||
@@ -61,8 +62,9 @@ class LargeDBSignatureCheck {
|
||||
val fullDBInputStream = javaClass.classLoader?.getResourceAsStream("nostr_vitor_startup_data.json.gz")
|
||||
|
||||
val eventArray =
|
||||
JacksonMapper.mapper.readValue<ArrayList<Event>>(
|
||||
JacksonMapper.mapper.readValue(
|
||||
GZIPInputStream(fullDBInputStream),
|
||||
jacksonTypeRefOf<ArrayList<Event>>(),
|
||||
) as List<Event>
|
||||
|
||||
var counter = 0
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@
|
||||
package com.vitorpamplona.quartz.nip06KeyDerivation
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
@@ -34,7 +34,7 @@ import org.junit.runner.RunWith
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class Bip39MnemonicsTest {
|
||||
private val tests =
|
||||
jacksonObjectMapper()
|
||||
ObjectMapper()
|
||||
.readTree(javaClass.classLoader?.getResourceAsStream("bip39.vectors.json"))
|
||||
|
||||
@Test
|
||||
|
||||
+4
-5
@@ -26,9 +26,8 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.InliningTagArrayPrettyPrinter
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.jacksonTypeRefOf
|
||||
import com.vitorpamplona.quartz.nip55AndroidSigner.api.foreground.intents.results.IntentResult
|
||||
import com.vitorpamplona.quartz.nip55AndroidSigner.api.foreground.intents.results.IntentResultJsonDeserializer
|
||||
import com.vitorpamplona.quartz.nip55AndroidSigner.api.foreground.intents.results.IntentResultJsonSerializer
|
||||
@@ -39,7 +38,7 @@ import java.io.InputStream
|
||||
|
||||
object JsonMapperNip55 {
|
||||
val defaultMapper: ObjectMapper =
|
||||
jacksonObjectMapper()
|
||||
ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
||||
.setDefaultPrettyPrinter(InliningTagArrayPrettyPrinter())
|
||||
@@ -51,9 +50,9 @@ object JsonMapperNip55 {
|
||||
.addSerializer(Permission::class.java, PermissionSerializer()),
|
||||
)
|
||||
|
||||
inline fun <reified T> fromJsonTo(json: String): T = defaultMapper.readValue<T>(json)
|
||||
inline fun <reified T> fromJsonTo(json: String): T = defaultMapper.readValue(json, jacksonTypeRefOf<T>())
|
||||
|
||||
inline fun <reified T> fromJsonTo(json: InputStream): T = defaultMapper.readValue<T>(json)
|
||||
inline fun <reified T> fromJsonTo(json: InputStream): T = defaultMapper.readValue(json, jacksonTypeRefOf<T>())
|
||||
|
||||
fun toJson(event: ArrayNode): String = defaultMapper.writeValueAsString(event)
|
||||
|
||||
|
||||
+11
-13
@@ -23,12 +23,10 @@ package com.vitorpamplona.quartz.nip01Core.jackson
|
||||
import com.fasterxml.jackson.core.json.JsonReadFeature
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.JavaType
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.OptimizedSerializable
|
||||
import com.vitorpamplona.quartz.nip01Core.core.RawJson
|
||||
@@ -64,7 +62,7 @@ class JacksonMapper {
|
||||
val defaultPrettyPrinter = InliningTagArrayPrettyPrinter()
|
||||
|
||||
val mapper =
|
||||
jacksonObjectMapper()
|
||||
ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, false)
|
||||
.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, false)
|
||||
@@ -104,13 +102,13 @@ class JacksonMapper {
|
||||
/**
|
||||
* Shortcuts
|
||||
*/
|
||||
val eventTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRef<Event>())
|
||||
val tagArrayTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRef<TagArray>())
|
||||
val rumorTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRef<Rumor>())
|
||||
val eventTemplateTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRef<EventTemplate<Event>>())
|
||||
val eventListTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRef<List<Event>>())
|
||||
val messageTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRef<Message>())
|
||||
val commandTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRef<Command>())
|
||||
val eventTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRefOf<Event>())
|
||||
val tagArrayTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRefOf<TagArray>())
|
||||
val rumorTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRefOf<Rumor>())
|
||||
val eventTemplateTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRefOf<EventTemplate<Event>>())
|
||||
val eventListTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRefOf<List<Event>>())
|
||||
val messageTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRefOf<Message>())
|
||||
val commandTypeInstance: JavaType = mapper.typeFactory.constructType(jacksonTypeRefOf<Command>())
|
||||
|
||||
fun fromJson(json: String): Event = mapper.readValue(json, eventTypeInstance)
|
||||
|
||||
@@ -169,12 +167,12 @@ class JacksonMapper {
|
||||
|
||||
inline fun <reified T : OptimizedSerializable> fromJsonTo(json: String): T {
|
||||
checkRegistered(T::class)
|
||||
return mapper.readValue<T>(json)
|
||||
return mapper.readValue(json, jacksonTypeRefOf<T>())
|
||||
}
|
||||
|
||||
inline fun <reified T : OptimizedSerializable> fromJsonTo(json: InputStream): T {
|
||||
checkRegistered(T::class)
|
||||
return mapper.readValue<T>(json)
|
||||
return mapper.readValue(json, jacksonTypeRefOf<T>())
|
||||
}
|
||||
|
||||
fun toJson(event: Event): String = EventManualSerializer.toJson(event.id, event.pubKey, event.createdAt, event.kind, event.tags, event.content, event.sig)
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.jackson
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
|
||||
/**
|
||||
* A [TypeReference] carrying the full generic type, e.g. `List<Event>`.
|
||||
*
|
||||
* This is the one thing the app used jackson-module-kotlin for that plain
|
||||
* jackson-databind has no equivalent of — and it is a one-liner, because `reified`
|
||||
* substitutes the concrete type into the anonymous subclass before erasure can
|
||||
* lose it. `T::class.java` cannot replace it: for `List<Event>` that erases to
|
||||
* `List`, and every element comes back a LinkedHashMap.
|
||||
*
|
||||
* Copied here rather than kept as a dependency so the Kotlin module — and
|
||||
* kotlin-reflect behind it, ~1,000 classes — can leave the app. The CLI still
|
||||
* depends on the module directly, because it genuinely binds reflectively and is
|
||||
* never minified.
|
||||
*/
|
||||
inline fun <reified T> jacksonTypeRefOf(): TypeReference<T> = object : TypeReference<T>() {}
|
||||
+2
-2
@@ -20,13 +20,13 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.jackson
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class InliningTagArrayPrettyPrinterTest {
|
||||
val mapper =
|
||||
jacksonObjectMapper().apply {
|
||||
ObjectMapper().apply {
|
||||
setDefaultPrettyPrinter(InliningTagArrayPrettyPrinter())
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
package com.vitorpamplona.quic.interop
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.vitorpamplona.quic.connection.EncryptionLevel
|
||||
import com.vitorpamplona.quic.observability.QlogObserver
|
||||
import java.io.BufferedWriter
|
||||
@@ -295,7 +294,7 @@ class QlogWriter(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_MAPPER: ObjectMapper = jacksonObjectMapper()
|
||||
private val DEFAULT_MAPPER: ObjectMapper = ObjectMapper()
|
||||
|
||||
private fun packetTypeFor(level: EncryptionLevel): String =
|
||||
when (level) {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.quic.interop
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.vitorpamplona.quic.connection.EncryptionLevel
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
@@ -73,7 +73,7 @@ class QlogWriterTest {
|
||||
val lines = tmp.readLines().filter { it.isNotBlank() }
|
||||
assertTrue(lines.size >= 12, "expected >= 12 lines (header + at least 11 events) but got ${lines.size}")
|
||||
|
||||
val mapper = jacksonObjectMapper()
|
||||
val mapper = ObjectMapper()
|
||||
|
||||
// Line 1: qlog header.
|
||||
val header = mapper.readTree(lines[0])
|
||||
@@ -128,7 +128,7 @@ class QlogWriterTest {
|
||||
w.onAlpnNegotiated("h3")
|
||||
}
|
||||
val lines = tmp.readLines().filter { it.isNotBlank() }
|
||||
val mapper = jacksonObjectMapper()
|
||||
val mapper = ObjectMapper()
|
||||
val event = mapper.readTree(lines[1])
|
||||
assertEquals(50L, event.get("time").asLong(), "time must be relative to constructor (1050 - 1000)")
|
||||
}
|
||||
@@ -152,7 +152,7 @@ class QlogWriterTest {
|
||||
QlogWriter(tmp, odcidHex = "00").use { w ->
|
||||
w.onPacketSent(EncryptionLevel.INITIAL, 0, 1200, emptyList())
|
||||
}
|
||||
val mapper = jacksonObjectMapper()
|
||||
val mapper = ObjectMapper()
|
||||
val lines = tmp.readLines().filter { it.isNotBlank() }
|
||||
val frames = mapper.readTree(lines[1]).get("data").get("frames")
|
||||
assertTrue(frames.isArray, "frames must be an array even when empty")
|
||||
|
||||
Reference in New Issue
Block a user