jackson-kotlin-serialization-specialist

作者: kotlin

診斷並設計 Kotlin 搭配 Jackson 在 Spring 應用程式中的 JSON 序列化與反序列化行為。適用於 DTO 無法反序列化、預設…

npx skills add https://github.com/kotlin/kotlin-backend-agent-skills --skill jackson-kotlin-serialization-specialist

Jackson Kotlin Serialization Specialist

Source mapping: Tier 2 high-value skill derived from Kotlin_Spring_Developer_Pipeline.md (SK-08).

Mission

Make Kotlin plus Jackson behavior explicit, compatible, and testable. Treat wire-format correctness as a contract problem, not only a mapper-configuration problem.

Read First

  • The actual DTO or event model classes.
  • The exact failing JSON payload or expected payload examples.
  • ObjectMapper customizers, Spring Boot Jackson properties, and any per-client mapper overrides.
  • Build files to verify Jackson module alignment with Spring Boot and Kotlin versions.
  • The boundary where serialization matters: MVC, WebFlux, Kafka, Redis, persistence JSON column, or external HTTP client.

Diagnose In This Order

  1. Verify module presence and alignment:
    • jackson-module-kotlin
    • JavaTimeModule
    • other custom modules or serializers
  2. Verify constructor semantics:
    • default parameters
    • required parameters
    • nullable versus non-null
  3. Verify field presence semantics:
    • absent
    • present with null
    • present with value
  4. Verify naming, inclusion, and date-time strategy.
  5. Verify polymorphism or custom serializer behavior.
  6. Verify whether the real bug comes from a local mapper override rather than the global mapper.

Core Kotlin Rules

  • Keep DTOs immutable unless the project already has a strong alternative convention.
  • Do not switch val to var just to appease Jackson.
  • Do not add empty constructors to Kotlin DTOs as a workaround if the Kotlin module can model the contract correctly.
  • Treat nullable as a wire-contract decision, not a convenient escape hatch.
  • Be explicit about value classes, sealed hierarchies, and default parameter behavior.

Advanced Serialization Traps

  • Missing field and explicit null are not the same. For PATCH-like contracts, model tri-state semantics deliberately.
  • Default constructor values can silently hide client mistakes if the field should have been required.
  • @JsonInclude may improve payload size but can also erase signal that clients rely on.
  • Non-null primitives, FAIL_ON_NULL_FOR_PRIMITIVES, and Kotlin non-null types interact differently across payload shapes.
  • Sealed classes need stable, versionable type discriminators. Do not treat polymorphic type ids as an internal detail once they are on the wire.
  • Enum serialization by name, code, or custom object form is a public compatibility choice.
  • Date-time serialization must make timezone assumptions explicit. Instant, OffsetDateTime, and LocalDateTime are not interchangeable.
  • @JvmInline value class support may differ by Jackson version and serializer context. Verify scalar form and map-key behavior explicitly.
  • Global ObjectMapper changes can break unrelated endpoints or message consumers. Prefer narrow fixes when the issue is boundary-specific.

Boundary-Specific Nuances

  • MVC request and response mapping, Kafka message mapping, Redis payload mapping, and JSON-column mapping often use different mapper lifecycles even inside one codebase.
  • ObjectMapper.copy() can preserve most configuration while still drifting from future global changes. If a subsystem owns a private mapper, document that divergence.
  • Kotlin default parameters interact differently with creator annotations, mix-ins, and custom deserializers. If custom deserialization exists, verify constructor invocation explicitly.
  • Unknown enum handling, unknown-property handling, and coercion rules are compatibility decisions. A permissive setting may preserve old clients or may quietly accept garbage.
  • If the API is documented through OpenAPI or consumer contracts, make sure the documented nullability and actual wire behavior match. Kotlin type hints alone are not enough.

Expert Heuristics

  • If the same model is used on both inbound and outbound boundaries, check whether the optimal serializer settings are actually symmetrical. Often they are not.
  • If clients rely on partial update semantics, prefer an explicit patch model rather than trying to infer intent from ordinary DTO nullability.
  • If a serializer bug appears after a dependency upgrade, inspect feature defaults and module registration order before rewriting DTOs.
  • If compatibility matters, prove the fix with golden JSON examples or snapshot-style serialization tests, not only with one happy-path request.

Design Rules

  • Choose one naming strategy and document it.
  • Keep transport DTOs separate from persistence and domain objects when contract stability matters.
  • If payload evolution matters, favor additive fields and backward-compatible defaults over silent semantic changes.
  • If multiple serialization contexts exist, decide which behavior is global and which is boundary-specific.

Output Contract

Return these sections:

  • Observed behavior: what the current mapper does.
  • Contract expectation: what the wire format should mean.
  • Root cause: module, DTO, annotation, or mapper configuration issue.
  • Minimal fix: the smallest safe code or config change.
  • Compatibility risk: what existing clients or consumers might notice.
  • Verification: tests or sample payloads that prove the behavior.

Guardrails

  • Do not recommend random Jackson versions outside the repository's version authority.
  • Do not add global mapper behavior for a local one-off issue without explaining blast radius.
  • Do not hide a contract problem behind broad JsonNode or Map<String, Any> usage unless the boundary is intentionally untyped.
  • Do not rely on Jackson defaults when a stable external contract matters.

Quality Bar

A good run of this skill explains the wire contract, the mapper mechanics, and the compatibility impact in one coherent answer. A bad run sprinkles annotations until the example payload passes while leaving the contract ambiguous or unstable.

來自 kotlin 的更多技能

kotlin-backend-jpa-entity-mapping
kotlin
Kotlin 的 data class 很適合用於 DTO,但對 JPA 實體來說卻很危險。Hibernate 依賴於 data class 所破壞的識別語義:基於所有欄位的 equals / hashCode 會在狀態變更後破壞 Set / Map 的成員資格,而自動生成的 copy() 則會建立受管實體的分離副本。
kotlin-tooling-agp9-migration
kotlin
Android Gradle Plugin 9.0 使得 Android 應用程式與函式庫外掛在同一個模組中與 Kotlin Multiplatform 外掛不相容。此技能將引導您完成遷移。
kotlin-tooling-cocoapods-spm-migration
kotlin
將 KMP 專案從 CocoaPods(kotlin("native.cocoapods"))遷移至 Swift Package Manager(swiftPMDependencies DSL)——將 pod() 替換為 swiftPackage(),…
kotlin-tooling-immutable-collections-0-5-x-migration
kotlin
將 Kotlin(及 Java)程式碼從 kotlinx.collections.immutable 0.3.x / 0.4.x 遷移至最新的 0.5.x 版本。0.5.x 系列重新命名了所有回傳副本的方法…
kotlin-tooling-java-to-kotlin
kotlin
使用嚴謹的四步驟轉換方法,將 Java 原始碼轉換為慣用的 Kotlin,並在每一步驟檢查五個不變量。支援框架感知轉換,能處理註解站點目標、函式庫慣用語及 API 保留。
kotlin-tooling-native-build-performance
kotlin
診斷並修復針對 iOS 的 Kotlin Multiplatform 專案中,Kotlin/Native 編譯與連結緩慢的問題。當使用者回報 iOS 或…速度緩慢時使用。
kotlin-spring-proxy-compatibility
kotlin
Diagnose and prevent Kotlin plus Spring proxy failures around `@Transactional`, `@Cacheable`, `@Async`, method security, retry, configuration proxies, and JPA…
ci-cd-containerization-advisor
kotlin
為 Kotlin 加 Spring 應用程式設計可重現的建置、映像檔與部署管線,包括 CI 驗證、分層容器、部署安全性……