jackson-module-kotlin の 2.10.0 と 2.10.1 でデフォルトの挙動が異なる
jackson-module-kotlinの2.10.0と2.10.1でデフォルトの挙動が異なる件を調べたメモです。
jackson-module-kotlinでis
prefixが付くbooleanのpropertyをシリアライズした場合の挙動が
2.10.1から変わったので注意が必要でした。
下記バージョンで試してみます。
- java 11.0.7
- kotlin 1.4.20
- jackson-databind 2.10.0
- jackson-module-kotlin 2.10.0
- jackson-databind 2.10.1
- jackson-module-kotlin 2.10.1
Dependency
gradleを使って試してみます。
spring bootで簡単なcontrollerを作成して試してみます。
javaでlombokの@Data
を指定した場合も比較したいのでlombokも追加してます。
build.gradle.kts
dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") compileOnly("org.projectlombok:lombok") annotationProcessor("org.projectlombok:lombok") testImplementation("org.springframework.boot:spring-boot-starter-test") { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") } }
mavenBomでjackson-bomのversionを明示的に指定します。
2.10.0と2.10.1を切り替えて試してみます。
dependencyManagement { imports { mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) { // extra["jackson-bom.version"] = "2.10.0" extra["jackson-bom.version"] = "2.10.1" } } }
java
javaでPersonクラスを定義してみます。
boolean
でisTestUser
とtestUser
を定義してみます。
それぞれlombokでも定義してみます。
isTestUser
フィールド
public static class JavaPerson { private final String name; private final boolean canWalk; private final boolean isTestUser; public JavaPerson( String name, boolean canWalk, boolean isTestUser ) { this.name = name; this.canWalk = canWalk; this.isTestUser = isTestUser; } public String getName() { return name; } public boolean isCanWalk() { return canWalk; } public boolean isTestUser() { return isTestUser; } }
testUser
フィールド
public static class JavaPersonNonIsPrefix { private final String name; private final boolean canWalk; private final boolean testUser; public JavaPersonNonIsPrefix( String name, boolean canWalk, boolean testUser ) { this.name = name; this.canWalk = canWalk; this.testUser = testUser; } public String getName() { return name; } public boolean isCanWalk() { return canWalk; } public boolean isTestUser() { return testUser; } }
@Data
でisTestUser
フィールド
@AllArgsConstructor @Data public static class JavaPersonUsingData { private String name; private boolean canWalk; private boolean isTestUser; }
@Data
でtestUser
フィールド
@AllArgsConstructor @Data public static class JavaPersonNonIsPrefixUsingData { private String name; private boolean canWalk; private boolean testUser; }
2.10.0
はじめに2.10.0で試してみます。
versionが2.10.0になってる事を確認。
$ gradle dependencies | | +--- com.fasterxml.jackson.core:jackson-databind:2.10.5 -> 2.10.0 | | | +--- com.fasterxml.jackson.core:jackson-annotations:2.10.0 | | | \--- com.fasterxml.jackson.core:jackson-core:2.10.0 | | +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.5 -> 2.10.0 | | | +--- com.fasterxml.jackson.core:jackson-core:2.10.0 | | | \--- com.fasterxml.jackson.core:jackson-databind:2.10.0 (*) | | +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.5 -> 2.10.0 | | | +--- com.fasterxml.jackson.core:jackson-annotations:2.10.0 | | | +--- com.fasterxml.jackson.core:jackson-core:2.10.0 | | | \--- com.fasterxml.jackson.core:jackson-databind:2.10.0 (*) | | \--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.5 -> 2.10.0 | | +--- com.fasterxml.jackson.core:jackson-core:2.10.0 | | \--- com.fasterxml.jackson.core:jackson-databind:2.10.0 (*) +--- com.fasterxml.jackson.module:jackson-module-kotlin -> 2.10.0 | +--- com.fasterxml.jackson.core:jackson-databind:2.10.0 (*) | +--- com.fasterxml.jackson.core:jackson-annotations:2.10.0
spring bootを起動して、それぞれを返すend pointにアクセスしてみます。
すべてtestUser
として返ってきています。
curl localhost:8080/java/vanilla | jq { "name": "test", "canWalk": true, "testUser": true }
curl localhost:8080/java/vanillaNonIsPrefix | jq { "name": "test", "canWalk": true, "testUser": true }
curl localhost:8080/java/usingData | jq { "name": "test", "canWalk": true, "testUser": true }
curl localhost:8080/java/nonIsPrefixUsingData | jq { "name": "test", "canWalk": true, "testUser": true }
2.10.1
今度は2.10.1で試してみます。
versionが2.10.1になってる事を確認。
gradle dependencies | | +--- com.fasterxml.jackson.core:jackson-databind:2.10.5 -> 2.10.1 | | | +--- com.fasterxml.jackson.core:jackson-annotations:2.10.1 | | | \--- com.fasterxml.jackson.core:jackson-core:2.10.1 | | +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.5 -> 2.10.1 | | | +--- com.fasterxml.jackson.core:jackson-core:2.10.1 | | | \--- com.fasterxml.jackson.core:jackson-databind:2.10.1 (*) | | +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.5 -> 2.10.1 | | | +--- com.fasterxml.jackson.core:jackson-annotations:2.10.1 | | | +--- com.fasterxml.jackson.core:jackson-core:2.10.1 | | | \--- com.fasterxml.jackson.core:jackson-databind:2.10.1 (*) | | \--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.5 -> 2.10.1 | | +--- com.fasterxml.jackson.core:jackson-core:2.10.1 | | \--- com.fasterxml.jackson.core:jackson-databind:2.10.1 (*) +--- com.fasterxml.jackson.module:jackson-module-kotlin -> 2.10.1 | +--- com.fasterxml.jackson.core:jackson-databind:2.10.1 (*) | +--- com.fasterxml.jackson.core:jackson-annotations:2.10.1
同様のクラスで試してみます。
すべてtestUser
として返ってきています。
curl localhost:8080/java/vanilla | jq { "name": "test", "canWalk": true, "testUser": true }
curl localhost:8080/java/vanillaNonIsPrefix | jq { "name": "test", "canWalk": true, "testUser": true }
curl localhost:8080/java/usingData | jq { "name": "test", "canWalk": true, "testUser": true }
curl localhost:8080/java/nonIsPrefixUsingData | jq { "name": "test", "canWalk": true, "testUser": true }
kotlin
kotlinでPersonクラスを定義してみます。
isTestUserというBooleanのpropertyを定義します。
普通のクラス
class KotlinPerson( val name: String, val canWalk: Boolean, val isTestUser: Boolean )
data class
data class KotlinPersonDataClass( val name: String, val canWalk: Boolean, val isTestUser: Boolean )
2.10.0
はじめに2.10.0で試してみます。
spring bootを起動して、それぞれを返すend pointにアクセスしてみます。
それぞれisTestUser
propertyがtestUser
として返ってきています。
これはjavaでのデシリアライズと同様の挙動です。
curl localhost:8080/kotlin/class | jq { "name": "test", "canWalk": true, "testUser": true }
curl localhost:8080/kotlin/dataclass | jq { "name": "test", "canWalk": true, "testUser": true }
2.10.1
今度は2.10.1で試してみます。
すると、2.10.0ではtestUser
で返ってましたが、2.10.1ではisTestUser
として返ってるのが分かります。
curl localhost:8080/kotlin/class | jq { "name": "test", "canWalk": true, "isTestUser": true }
curl localhost:8080/kotlin/dataclass | jq { "name": "test", "canWalk": true, "isTestUser": true }
これはjackson-module-kotlin 2.10.1で入った変更です。
https://github.com/FasterXML/jackson-module-kotlin/pull/256
https://github.com/FasterXML/jackson-module-kotlin/issues/80
この変更はjavaのクラスをkotlinに移行する時に困る場合があります。
(テストを入れてば検知出来ますが)
javaのis
prefixのbooleanのフィールドをそのままkotlinのpropertyとして移行すると、
突然デシリアライズの値がisXxxx
で返ってくることになります。
@JsonProperty
2.10.1以降でproperty名を変更せずにデシリアライズのプロパティを変更するには
@JsonProperty
で名前を明示的に指定してあげればよいです。
class KotlinPersonWithJsonProperty( val name: String, val canWalk: Boolean, @get:JsonProperty("testUser") val isTestUser: Boolean )
試してみると、testUser
として返ってきてます。
curl localhost:8080/kotlin/dataclassWithJsonProperty | jq { "name": "test", "canWalk": true, "testUser": true }
サンプルコードは下記にあげました。
おわり。