Blog

【解決済み】jackson 2.7 以後は @ConstructorProperties を参照するようになっているので、lombok を使っている場合に何も考えずにアップグレードすると死ぬ

Jackson 2.7 がリリースされていますが、このバージョンから @ConstructorProperties を参照するようになっています。

See

@java.beans.ConstructorProperties は、Java Beans の標準にある annotation です。 これにより、引数を持つコンストラクタを Jackson で利用できるようになります。

たとえば、以下のように、 immutable なオブジェクトを Jackson で扱えるようになるのです!

public class Point {
    @ConstructorProperties({"x", "y"})
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    private final int x, y;
}

そういうわけで、便利なんですが、ここに一つ落とし穴があります。

lombok の @AllArgsConstructor または @Value を利用していて、かつ @JsonProperty でフィールド名を変更している場合です。 つまり、以下のようなケース。

@AllArgsConstructor
@Value
public class Foo {
    @JsonProperty("y")
    private String x;
}

この場合、生成コードは以下のようなものになります。

public class Foo {
    @JsonProperty("y")
    private String x;

    public String getX() { return this.x; }

    @ConstructorProperties({"x"})
    public Foo(String x) { return this.x; }
}

で、こうなった時に、Jackson 氏は @ConstructorProperties@JsonProperty で割り当てられてる名前が違うやんけ! と文句を言ってくるわけです。

再現コードは以下のようになります。

import java.util.List;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;

import lombok.AllArgsConstructor;
import lombok.Data;

public class Boo {
    @Data
    @AllArgsConstructor
    public static class Foo {
        @JsonProperty("foo_id")
        private String fooId;
    }

    public static void main(String[] args) {
        ObjectMapper objectMapper = new ObjectMapper();
        JavaType javaType = objectMapper.getTypeFactory().constructType(Foo.class);
        BeanDescription desc = objectMapper.getSerializationConfig().introspect(javaType);
        List<BeanPropertyDefinition> properties = desc.findProperties();
        System.out.println(properties);
    }
}

で、この挙動を抑制するには、@ConstructorProperties の付与を停止すればよいです。

プロジェクトのルート・ディレクトリに lombok.config というファイルを配置します。 build.gradle と同じディレクトリにおけばOK.

このファイルに以下のように記述すれば、@ConstructorProperties の生成が抑制されるので問題なくなる。

lombok.anyConstructor.suppressConstructorProperties = true

そんな感じです。

【2016.09.08 追記】 最新版では問題発生しなくなっています。