Jackson CamelCase フィールドで、UnrecognizedPropertyException

Jackson で、次のような JSON をデシリアライズすると UnrecognizedPropertyException が発生

{
   "firstName": "太郎",
   "lastName" : "山田",
   "uId": "A001"
}
import lombok.Data;

@Data
public class UserInfo{
     private String firstName;
     private String lastName;
     private String uId;
}

UsetInfo にデシリアライズのため、

ObjectMapper mapper = new ObjectMapper();
UserInfo userinfo = mapper.readValue(jsontxt, UserInfo.class);

で実行すると、、
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "uId"

Google gson ではこんなこと起きない!!

先頭から2文字目が大文字になる uId はダメで、他の CamelCase である firstName や lastName では発生しない!

lombok がいけないのか?
確かに lombok だと getter は、 public String getUId() で setter は public void setUId(String uId) で
Eclipse なんかで getter/setter 生成が作る
public String getuId() / public void setuId(String uId)
とは違って、2文字目が大文字のケース特有とは異なる。この getuId / setuId を自分で書けば
問題なく動くが、それでは lombok を使う意味がない。このフィールドだけ lombok を使わない解決方法は
なんか的はずれな気がする。
lombok を使うとしての解決策は、、、

java - How to specify jackson to only use fields - preferably globally - Stack Overflow
を読むと、、
デフォルトのJacksonのは、プロパティ(getter と setter)とフィールドの両方を使用して、
jsonシリアライズおよびデシリアライズすることが理由であるかのように
書いているけど、これではちゃんと説明にならない。でも解決策が導きだされている。

ObjectMapper に、Visibility の設定を行うと良いのだそうだ。。。

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();

mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

UserInfo userinfo = mapper.readValue(jsontxt, UserInfo.class);

これで、うまく動く!

別の解決策としては、@JsonProperty を指定して強制的にフィールドを紐つける方法で、

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

@Data
public class UserInfo{
     private String firstName;
     private String lastName;
     @JsonProperty private String uId;
}

これなら、mapper.setVisibility を実行しなくても良いのだが、
先頭から2文字大文字の camelCase のフィールドの都度、
アノテーションをつけるなんてセンスが悪すぎる。

ObjectMapper mapper = new ObjectMapper();

mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

この形を覚えておいた方が良いと思われる。