[Jackson] json 変換時に isXXX プロパティを無視したい
Java オブジェクトで、フィールド名が isXXX かつ ゲッター名が isXXX となっているフィールドを、シリアライズ対象から外す方法。lombok の @Accessors(fluent = true) と @JsonIgnore の組み合わせだとうまくいかなかった。
やりたいこと
Student オブジェクトを 下記の形で json 変換させること。
json
{"id":999,"grade":3,"className":"もも","name":"Jackson"}
Student オブジェクト
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private int grade;
@Column(nullable = false)
private String className;
@Column(nullable = false)
private String name;
// シリアライズ対象外
@Column(nullable = false)
@Accessors(fluent = true)
private Boolean isActive;
@JsonIgnore
@Column(nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private ZonedDateTime created;
@JsonIgnore
@Column(nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
private ZonedDateTime updated;
}
isActive は、 lombok の @Accessors(fluent = true) を使って isGetter と isSetter を自動生成している。
ダメな例
シリアライズ対象外のフィールドに @JsonIgnore をつける。
Student オブジェクト
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private int grade;
@Column(nullable = false)
private String className;
@Column(nullable = false)
private String name;
@Column(nullable = false)
@Accessors(fluent = true)
@JsonIgnore // ← ここに付与した
private Boolean isActive;
@JsonIgnore
@Column(nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private ZonedDateTime created;
@JsonIgnore
@Column(nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
private ZonedDateTime updated;
}
結果
json 変換して出力してみる。
void convertObjectToJson() throws JsonProcessingException {
Student object = new Student();
object.setId(999L);
object.setGrade(3);
object.setClassName("もも");
object.setName("Jackson");
object.isActive(true);
object.setCreated(ZonedDateTime.now());
object.setUpdated(ZonedDateTime.now());
String jsonStr = objectMapper.writeValueAsString(object);
System.out.println("JSON : " + jsonStr);
}
ΣΣ(゚д゚lll) エッ?
active というプロパティが含まれる。
JSON : {"id":999,"grade":3,"className":"もも","name":"Jackson","active":true}
うまくいく例
@JsonIgnore ではなく
@JsonAutoDetect を使う。
isGetterVisibility オプションがあるので、Visibility.NONE に指定する。
これを付与することで、Jackson は isGetter を検出しなくなる。
注意
@JsonAutoDetect はクラスに付与するものなので、全ての isGetter を検出しなくなる。
Student オブジェクト
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
// ↓これを付与した
@JsonAutoDetect(isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private int grade;
@Column(nullable = false)
private String className;
@Column(nullable = false)
private String name;
@Column(nullable = false)
@Accessors(fluent = true)
private Boolean isActive;
@JsonIgnore
@Column(nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private ZonedDateTime created;
@JsonIgnore
@Column(nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
private ZonedDateTime updated;
}
結果
変換された json を出力。
JSON : {"id":999,"grade":3,"className":"もも","name":"Jackson"}
なぜ?
明確な原因は分からなかったが、おそらく以下ではないかと思った。
ゲッター名が is から始まると、 is を取り除いた部分をプロパティ名として返すようだ。
https://github.com/FasterXML/jackson-module-kotlin/issues/80
おそらくこれが原因で active が json に含まれた。 @JsonIgnore は isActive に付いているため、 active はシリアライズの除外対象に含まれないのではないかと考える。
is から始まらないゲッターの場合
では、 canXXX など、isGetter ではないゲッターだとどう動くのか試してみた。
Student オブジェクトに @JsonAutoDetect なしで、↓を追加した。
@Accessors(fluent = true)
private Boolean canActive;
出力された json
JSON : {"id":999,"grade":3,"className":"もも","name":"Jackson","active":true}
ゲッター名の始まりが get でも is でもない場合、Jackson はプロパティを検出しないようだ。
次に @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) を付与して、フィールドを検出可能にしてみた。
出力された json
JSON : {"id":999,"grade":3,"className":"もも","name":"Jackson","isActive":true,"canActive":true,"active":true}
canActive は検出された。
isActive と active は両方 json 出力されているので、ゲッター名が is から始まる場合には、フィールド名とゲッター名が別物として検出しているようだ。