Skip to content

chore: simplify TristateModule type-resolution and serializer wiring#184

Merged
OmarAlJarrah merged 2 commits into
mainfrom
chore/simplify-tristate-module
Jun 25, 2026
Merged

chore: simplify TristateModule type-resolution and serializer wiring#184
OmarAlJarrah merged 2 commits into
mainfrom
chore/simplify-tristate-module

Conversation

@OmarAlJarrah

Copy link
Copy Markdown
Member

Summary

Three behavior-preserving cleanups to TristateModule.kt in sdk-serde-jackson. They cluster around the same file — repeated JavaType resolution boilerplate and a serializer hook that earns nothing.

The Tristate<T> round-trip contract is untouched: Absent omits the key, Null serializes as JSON null, Present(v) serializes as v, and inner-type resolution is identical. Every class involved is internal, so there is no public-API change and apiCheck is unaffected.

Changes

  • Extract ANY_TYPE, firstContainedOrNull(), isTristate() helpers. The deserializer side resolved JavaTypes in three places (findBeanDeserializer, createContextual, deserialize), each repeating the same trio: the Tristate assignability guard, the "first contained type or fall back to Object" lookup, and the constructType(Object) fallback. Naming each step once removes the copy-paste and makes the type-resolution intent obvious at every call site.

  • Drop the no-op ContextualSerializer from TristateSerializer. Its createContextual just returned this, which is exactly how Jackson treats a non-contextual serializer. The serializer already resolves the payload type per value through its PropertySerializerMap, so there is no per-property type to capture at contextualization time. The unused import is removed in the same change. The deserializer's ContextualDeserializer is genuine (it captures the property's T) and stays.

  • Rewrite changeProperties with List.replaceAll. The wrap-or-keep decision now reads as one expression instead of a forEachIndexed loop that mutates the list it is iterating.

Verification

./gradlew :sdk-serde-jackson:build passes — tests, ktlint, detekt, and apiCheck all green.

Closes #181

Extract the repeated JavaType-resolution boilerplate in the Tristate
deserializer path into three file-private helpers — ANY_TYPE (the Object
fallback for raw/Tristate<*>), JavaType.firstContainedOrNull(), and
JavaType.isTristate() — so the inner-type lookup reads the same way at
each of its call sites instead of being open-coded three times.

Drop the no-op ContextualSerializer from TristateSerializer: its
createContextual just returned this, which is how Jackson treats a
non-contextual serializer anyway. Payload typing is already resolved
per value through the dynamic PropertySerializerMap, so there is no
per-property type to capture. The deserializer's ContextualDeserializer
is genuine and stays.

Rewrite TristateSerializerModifier.changeProperties with List.replaceAll
so the wrap-or-keep decision is a single expression rather than an
indexed loop mutating the list as it iterates.

All three are behavior-preserving; every type involved is internal, so
there is no public-API change.
Relocate ANY_TYPE, JavaType.firstContainedOrNull(), and
JavaType.isTristate() out of TristateModule.kt into a dedicated
Extensions.kt, and drop the now-unused TypeFactory import from
TristateModule.kt. The helpers stay internal — they are module-private
implementation details, not public API.
@OmarAlJarrah OmarAlJarrah merged commit a651bdc into main Jun 25, 2026
@OmarAlJarrah OmarAlJarrah deleted the chore/simplify-tristate-module branch June 25, 2026 21:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sdk-serde-jackson: simplify TristateModule type-resolution and serializer wiring

1 participant