LoginSignup
0
0

Kotlin + Jackson (+@field:JsonProperty)のシリアライズで属性が複数出力されてしまう件

Posted at

現象

次のような data class があったとする。

Foo.kt
data class Foo(
    @field:JsonProperty("iPhone")
    val iPhone: Boolean
)

これを Jackson を使用してシリアライズすると以下のようになる。

{"iPhone":true,"iphone":true}

小文字1文字 + 大文字ではじまるプロパティの場合にこの挙動となる。

原因

(Jackson の中の方までは追っていないので、おそらくという感じですが)

iPhone のようなプロパティは、class ファイル上では(というか Java からは)、以下のようになる。

public final class Foo {
    @JsonProperty
    private final boolean iPhone;
    public final boolean getIPhone() { ... };
}

getter の名称がプロパティ名の1文字目を大文字にしたものとなっている。
これ自体は getiPhone というメソッド名とされるよりも、まったく見やすいものなのでおかしくはない。

しかし、JavaBeans 仕様に従いプロパティ名が決定されると、この getter によるプロパティの名称は iphone となる。
これはgetURL() のようなメソッドがあった場合に、そのプロパティ名が uRL となってしまわないように、先頭で連続する大文字はすべて小文字にする仕様となっているからだと思う(ちょっと正確には覚えていない)

このため、iPhone -> getIPhone -> iphone となり、Jackson は iphone という属性を出力する。

一方で @field:JsonProperty("iPhone") があるので、Jackson はこの指定にも従いフィールドを iPhone という属性で出力する。

ちなみに、Jackson は getter から導出されるプロパティ名から実際に値を持っているフィールドを、本来はちゃんと推測して関連付けているっぽいので、以下のようなプロパティの場合は2つ出たりしない。

@field:JsonProperty("bar")
val foo: String

また、通常であれば Jackson はフィールドを出力対象としていないので、@field:JsonProperty がなければ(あるいは @get:JsonProperty であれば)、やはり2つ出たりはしない。

data class Foo(
    val iPhone: Boolean // {"iphone":true} が出力される
)
data class Foo(
    @get:JsonProperty("iPhone") // {"iPhone":true} が出力される
    val iPhone: Boolean
)

その他

OpenAPI Generator を使用して kotlin 用のコードを生成した場合に、この問題が発生した。

モデル生成(リクエストやレスポンスで使用する DTO)のテンプレートが、data class のプロパティに @field:JsonProperty を付けているので、そこが @get:JsonProperty だったら良かったのに……となった。
テンプレートを入れ替える方法もあるが、あまりカスタマイズ箇所を増やしたくなかったのもあって、プロパティ名の方を変えることで対処した。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0