FasterXML / FasterXML/jackson-module-kotlin
Add KotlinFeature to exclude compiler-generated `$delegate` backing fields from serialization
- Lingua principale
- Kotlin
- Stelle
- 1.2k
- Fork
- 187
- Merge medio
- 9h 48m
- PR unite (30g)
- 16
Descrizione
### Use case
We log entity classes as JSON before saving them to the database for auditing purposes. Our entities use Kotlin's `by lazy`
for computed properties, and our Jackson ObjectMapper is configured with field visibility `ANY` to serialize all fields
without requiring explicit annotations.
The problem is that compiler-generated `$delegate` backing fields leak into the serialized JSON output.
```kotlin
data class User(val id: Long, val name: String) {
val displayName: String by lazy { "$name#$id" }
}
```
Serialized output:
```kotlin
{
"id": 1,
"name": "Alice",
"displayName": "Alice#1",
"displayName$delegate": {
"_initializer": {},
"_value": "Alice#1"
}
}
```
The `displayName$delegate` field exposes Kotlin's internal implementation details — including the `_initializer` lambda
reference — into JSON output. Since `$delegate` fields are a Kotlin compiler artifact that Java never produces, and `by lazy`
is used pervasively in Kotlin, this belongs as a built-in option in the Kotlin module.
### Describe the solution you'd like
Add a new `KotlinFeature` option to opt-in to automatic exclusion of `$delegate` fields:
```kotlin
val mapper = JsonMapper.builder()
.addModule(
KotlinModule.Builder()
.enable(KotlinFeature.IgnoreDelegateProperties)
.build()
)
.build()
```
The implementation is minimal (~15 lines) — register a `ValueSerializerModifier` inside `KotlinModule.setupModule()` when the feature is enabled:
```kotlin
if (ignoreDelegateProperties) {
context.addBeanSerializerModifier(object : ValueSerializerModifier() {
override fun changeProperties(
config: SerializationConfig,
beanDesc: BeanDescription.Supplier,
beanProperties: MutableList
): MutableList {
beanProperties.removeIf { it.name.endsWith("\$delegate") }
return beanProperties
}
})
}
```
Opt-in, disabled by default — fully backward-compatible
No new dependencies — simple name-suffix check, no kotlinx-metadata-jvm needed
-No AnnotationIntrospector changes — uses ValueSerializerModifier (addresses the concern from #459 about AI's property-by-property design)
- Follows existing patterns — fits naturally alongside other KotlinFeature options like NullToEmptyCollection and SingletonSupport
I'd be happy to submit a PR if this approach is acceptable.
### Describe alternatives you've considered
1. @JsonIgnore on delegate field — Not possible in Kotlin (#25)
2. MixIn classes — Requires a separate MixIn per class, doesn't scale when `by lazy` is used across many classes
3. @JsonIncludeProperties — Requires explicitly listing every property per class, fragile when fields are added/removed
4. @param:JsonProperty(access = WRITE_ONLY) — Must be added to every lazy declaration individually
5. Custom ValueSerializerModifier (current workaround) — Works, but every Kotlin project must independently discover and implement this same boilerplate
### Additional context
Related issues: #459, #574, #471, #71, #25
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Valutazione
Questa issue non è ancora stata valutata.