現象
次のような data class
があったとする。
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
だったら良かったのに……となった。
テンプレートを入れ替える方法もあるが、あまりカスタマイズ箇所を増やしたくなかったのもあって、プロパティ名の方を変えることで対処した。