jackson-module-kotlin
jackson-module-kotlin copied to clipboard
ObjectMapper serialization for boolean fields named isFieldName changed between versions 2.10.5 and 2.12.4
(NOTE: transferred from "jackson-databind", assumed Kotlin-module specific)
Describe the bug
I'm upgrading a project from Jackson 2.10.5 to 2.12.4 and one of the classes started serializing differently. The class has a field named isOpen
which is not a boolean. In 2.10.5 this serialized as "isOpen": { }
In 2.12.4 the field is dropped entirely.
Version information 2.12.4
To Reproduce
This first test passes with Jackson 2.10.5 and fails in 2.12.4. The second test passes in both versions.
class JacksonTest {
@Test
fun `will serialize a data class with a property named isOpen if it is not a boolean`() {
data class Thing(val isOpen: Map<String, Boolean>)
val data = Thing(isOpen = mapOf(Pair("a", true)))
ObjectMapper().writeValueAsString(data) shouldBe """{"isOpen":{"a":true}}"""
}
@Test
fun `will serialize a data class with a property named isOpen if it is a boolean`() {
data class Thing(val isOpen: Boolean)
val data = Thing(isOpen = true)
ObjectMapper().writeValueAsString(data) shouldBe """{"open":true}"""
}
}
@kyleboon Is this behaviour mentioned anywhere in any CHANGELOG
? I couldn't find anything but can confirm that I am also seeing the same behaviour while updating form version 2.9.8 to 2.12.4.
Is this with Kotlin? If so, the right project is jackson-module-kotlin
, not jackson-databind
-- I can transfer but want to make sure first. Kotlin's logic for "is propeties" differs from Java Beans spec and there have been changes in this area.
Sorry for the late response, yes this was with kotlin
Same happening with Java as well.
@sudo-nan0-RaySK I don't think this should be the case, unless Kotlin module is registered. If it does occur, this would belong here, but for now I assume it is Kotlin-MODULE specific (not necessarily language/value however; but module does not discriminate).
In 2.15 or later, the following is the correct behavior when using KotlinModule
.
import org.junit.Test
import kotlin.test.assertEquals
class JacksonTest {
@Test
fun `will serialize a data class with a property named isOpen if it is not a boolean`() {
data class Thing(val isOpen: Map<String, Boolean>)
val data = Thing(isOpen = mapOf(Pair("a", true)))
assertEquals("""{"isOpen":{"a":true}}""", jacksonObjectMapper().writeValueAsString(data))
}
@Test
fun `will serialize a data class with a property named isOpen if it is a boolean`() {
data class Thing(val isOpen: Boolean)
val data = Thing(isOpen = true)
assertEquals("""{"isOpen":true}""", jacksonObjectMapper().writeValueAsString(data))
}
}
This issue is closed because the default behavior has changed.
The above test still fails for me.
@geardaddie Please share the test cases and the versions you used.
tl;dr - Using 2.15.1 without registering the KotlinModule, test still fails as Kyle described. Using 2.15.1 with registering the KotlinModule inverts the pass/fail results with it no longer serializing the property isOpen as "open"
Ah.. I missed that the test was changed. It now uses "isOpen" in both cases vs using "open" for boolean variables. Isn't that a breaking change?
For reference: I'm using Kotlin 1.8.20, Jackson 2.15.1
It looks like I get the proper "boolean" behavior if I don't register the Kotlin module, but the serialization of the map starting with "isOpen" doesn't work (works as it did in the original post). If I register the Kotlin module, then the "isOpen" map works correctly, but the boolean "isOpen" is written as "isOpen".
As for test case, I'm using exactly what Kyle posted originally.
Only I've tried changing is instead of using "ObjectMapper()", I tried registering the Kotlin Module "ObjectMapper()
.registerKotlinModule()"
@k163377 I'm actually getting the above test to fail with 2.10.5 with a parse error when using the Kotlin module. So... hold off on doing any digging.
Error on 2.10.5 with the Kotlin module registered
Signature Parse error: expected '<' or ';' but got
Remaining input: - will serialize a data class with a property named isOpen if it is not a boolean$Thing;
java.lang.reflect.GenericSignatureFormatError: Signature Parse error: expected '<' or ';' but got
Remaining input: - will serialize a data class with a property named isOpen if it is not a boolean$Thing;
Wrt behavior differing between Kotlin module being registered and not -- Kotlin's handling of "is-getters" is fundamentally different from Java Bean naming. So difference in that sense is expected. Changes otherwise intend to get closer to Kotlin semantics so -- in general -- later versions' behavior is more correct and even if breaking are towards getting things to work better (fixing problems). That is not always what ends up happening of course, but the intent.