[Jackson]  json 変換時に isXXX プロパティを無視したい

JavaSpringBoot

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) を使って isGetterisSetter を自動生成している。

ダメな例

シリアライズ対象外のフィールドに @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 に含まれた。 @JsonIgnoreisActive に付いているため、 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 は検出された。

isActiveactive は両方 json 出力されているので、ゲッター名が is から始まる場合には、フィールド名とゲッター名が別物として検出しているようだ。

Posted by Agopeanuts