programing

Jackson과 Lombok이 함께 작동하도록 만들 수 없습니다.

copyandpastes 2021. 1. 15. 20:19
반응형

Jackson과 Lombok이 함께 작동하도록 만들 수 없습니다.


나는 Jackson과 Lombok을 결합하는 실험을하고 있습니다. 내 수업은 다음과 같습니다.

package testelombok;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.experimental.Wither;

@Value
@Wither
@AllArgsConstructor(onConstructor=@__(@JsonCreator))
public class TestFoo {
    @JsonProperty("xoom")
    private String x;
    private int z;
}
package testelombok;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xebia.jacksonlombok.JacksonLombokAnnotationIntrospector;
import java.io.IOException;

public class TestLombok {

    public static void main(String[] args) throws IOException {
        TestFoo tf = new TestFoo("a", 5);
        System.out.println(tf.withX("b"));
        ObjectMapper om = new ObjectMapper().setAnnotationIntrospector(new JacksonLombokAnnotationIntrospector());
        System.out.println(om.writeValueAsString(tf));
        TestFoo tf2 = om.readValue(om.writeValueAsString(tf), TestFoo.class);
        System.out.println(tf2);
    }

}

그것들은 내가 classpth에 추가하는 JAR입니다.

나는 그것을 Netbeans로 컴파일하고 있습니다 (나는 이것이 정말로 관련이 있다고 생각하지 않지만 어쨌든 완벽하고 충실하게 재현 가능하게 만들기 위해 이것을보고하고 있습니다). 위의 5 개 JAR lib은 프로젝트 폴더 내의 " " 폴더에 보관됩니다 ( " src", " nbproject", " test"및 " build"과 함께). 프로젝트 속성 의 " Add JAR / Folder "버튼을 통해 Netbeans에 추가 했으며 위 목록과 동일한 순서로 나열됩니다. 프로젝트는 표준 "Java 애플리케이션"유형 프로젝트입니다.

또한 Netbeans 프로젝트는 " 저장시 컴파일하지 않음 ", " 디버깅 정보 생성 ", " 사용되지 않는 API보고 ", " 자바 종속성 추적 ", " 활성화 주석 처리 "및 " 편집기에서 주석 처리 활성화 "로 구성됩니다. Netbeans에는 주석 프로세서 또는 주석 처리 옵션이 명시 적으로 구성되어 있지 않습니다. 또한 " -Xlint:all"명령 줄 옵션이 컴파일러 명령 줄에 전달되고 컴파일러는 외부 VM에서 실행됩니다.

내 javac의 버전은 1.8.0_72이고 Java의 버전은 1.8.0_72-b15입니다. 내 Netbeans는 8.1입니다.

내 프로젝트가 잘 컴파일됩니다. 그러나 실행시 예외가 발생합니다. 예외는 쉽게 보이거나 명백하게 고칠 수있는 것으로 보이지 않습니다. 다음은 stacktrace를 포함한 출력입니다.

TestFoo(x=b, z=5)
{"z":5,"xoom":"a"}
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Argument #0 of constructor [constructor for testelombok.TestFoo, annotations: {interface java.beans.ConstructorProperties=@java.beans.ConstructorProperties(value=[x, z]), interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
 at [Source: {"z":5,"xoom":"a"}; line: 1, column: 1]
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:296)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:269)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
    at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:475)
    at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3890)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3785)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2833)
    at testelombok.TestLombok.main(TestLombok.java:14)
Caused by: java.lang.IllegalArgumentException: Argument #0 of constructor [constructor for testelombok.TestFoo, annotations: {interface java.beans.ConstructorProperties=@java.beans.ConstructorProperties(value=[x, z]), interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._addDeserializerConstructors(BasicDeserializerFactory.java:511)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:323)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:253)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:219)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:141)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:406)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:352)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
    ... 7 more

나는 이미 @Value@AllArgsConstructor주석으로 무작위로 찌르려고 시도했지만 더 나아질 수 없었습니다.

나는 예외를 구글에서 발견했고 jackson에 대한 오래된 버그 보고서열려 있지만 다른 것과 관련된 것으로 보이는 다른 버그 보고서를 발견했습니다 . 그러나 이것은 여전히이 버그가 무엇인지 또는 수정 방법에 대해 알려주지 않습니다. 또한 다른 곳에서 유용한 것을 찾을 수 없었습니다.

내가하려는 것은 lombok과 jackson의 매우 기본적인 사용법이기 때문에이 문제를 해결하는 방법에 대한 유용한 정보를 더 이상 찾을 수 없다는 것이 이상하게 보입니다. 내가 뭔가를 놓친 것일까?

" 롬복을 사용하지 마십시오 "또는 " 잭슨을 사용하지 마십시오 " 라고 말하는 것 외에 ,이 문제를 해결하는 방법에 대해 아는 사람이 있습니까?


불변하지만 lombok과 jackson을 사용하는 json 직렬화 가능한 POJO를 원한다면. 롬복 빌더에서 jacksons 새 주석을 사용하십시오. @JsonPOJOBuilder(withPrefix = "")이 솔루션을 시도했으며 매우 잘 작동합니다. 샘플 사용법

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import lombok.Builder;
import lombok.Value;

@JsonDeserialize(builder = Detail.DetailBuilder.class)
@Value
@Builder
public class Detail {

    private String url;
    private String userName;
    private String password;
    private String scope;

    @JsonPOJOBuilder(withPrefix = "")
    public static class DetailBuilder {

    }
}

너무 많은 클래스가 @Builder있고 상용구 코드가 비어있는 주석을 원하지 않는 경우 주석 인터셉터가 비어 있도록 재정의 할 수 있습니다.withPrefix

mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
        @Override
        public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) {
            if (ac.hasAnnotation(JsonPOJOBuilder.class)) {//If no annotation present use default as empty prefix
                return super.findPOJOBuilderConfig(ac);
            }
            return new JsonPOJOBuilder.Value("build", "");
        }
    });

그리고 @JsonPOJOBuilder어노테이션이 있는 빈 빌더 클래스를 제거 할 수 있습니다 .


나는 suppressConstructorProperties = true매개 변수 를 추가하여 (귀하의 예를 사용하여) 똑같은 문제를 "해결"했습니다 .

@Value
@Wither
@AllArgsConstructor(suppressConstructorProperties = true)
public class TestFoo {
    @JsonProperty("xoom")
    private String x;
    private int z;
}

Jackson은 분명히 java.beans.ConstructorProperties 생성자에 추가 주석을 좋아하지 않습니다 . suppressConstructorProperties = true매개 변수는 말한다 롬복을 (그것이 기본적으로 수행)을 추가 할 수 없습니다.


Immutable + Lombok + Jackson은 다음 방법으로 달성 할 수 있습니다.

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Value;

@Value
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@AllArgsConstructor
public class LocationDto {

    double longitude;
    double latitude;
}

class ImmutableWithLombok {

    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        String stringJsonRepresentation = objectMapper.writeValueAsString(new LocationDto(22.11, 33.33));
        System.out.println(stringJsonRepresentation);

        LocationDto locationDto = objectMapper.readValue(stringJsonRepresentation, LocationDto.class);
        System.out.println(locationDto);
    }
}

@AllArgsConstructor(suppressConstructorProperties = true)더 이상 사용되지 않습니다. lombok.anyConstructor.suppressConstructorProperties=true( https://projectlombok.org/features/configuration )을 정의 하고 POJO의 롬복 주석 @Value@Data+ @NoArgsConstructor+로 변경 @AllArgsConstructor하십시오.


나는 위의 몇 가지를 시도했지만 모두 기질이 좋았습니다. 저에게 정말로 효과가 있었던 것은 제가 여기서 찾은 답 입니다.

프로젝트의 루트 디렉토리에 파일을 추가하십시오 (아직 수행하지 않은 경우).

lombok.config

안에 붙여 넣으세요

lombok.anyConstructor.addConstructorProperties=true

그런 다음 다음과 같이 pojo를 정의 할 수 있습니다.

@Data
@AllArgsConstructor
public class MyPojo {

    @JsonProperty("Description")
    private String description;
    @JsonProperty("ErrorCode")
    private String errorCode;
}

나를 위해 lombok 버전을 'org.projectlombok : lombok : 1.18.0'으로 업데이트했을 때 작동했습니다.


"mixin"패턴 을 사용하면 Jackson이 거의 모든 것을 가지고 놀 수 있습니다 . 기본적으로 클래스를 실제로 수정하지 않고 기존 클래스에 Jackson 주석을 추가하는 방법을 제공합니다. 잭슨이 Jackson 기능에 대해 가지고있는 문제를 해결하기 때문에 Lombok 솔루션보다는 여기에서 권장하는쪽으로 기울고 있습니다. 따라서 장기적으로 작동 할 가능성이 더 큽니다.


이 모듈도 필요합니다. https://github.com/FasterXML/jackson-modules-java8

그런 다음 컴파일러에 대한 -parameters 플래그를 켭니다.

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>

나는 모든 수업에 다음과 같이 주석을 달았습니다.

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
@Data
@Accessors(fluent = true)
@NoArgsConstructor
@AllArgsConstructor

적어도 2 년 동안 모든 Lombok 및 Jackson 버전에서 작동했습니다.

예:

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
@Data
@Accessors(fluent = true)
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    String id;
    String first;
    String last;
}

그리고 그게 다야. 롬복과 잭슨은 마치 매력처럼 함께 연주합니다.


@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
public class Person {
   String id;
   String first;
   String last;
}

데이터 클래스 외에 ObjectMapper를 올바르게 구성해야합니다. 이 경우 ParameterNamesModule 구성으로 정상적으로 작동하고 필드 및 생성자 메서드의 가시성을 설정합니다.

    om.registerModule(new ParameterNamesModule());
    om.setVisibility(FIELD, JsonAutoDetect.Visibility.ANY);
    om.setVisibility(CREATOR, JsonAutoDetect.Visibility.ANY);

그러면 예상대로 작동합니다.


나는 ConstructorProperies주석을 추가하지 않도록 Lombok을 얻는 데 문제가 있었기 때문에 반대 방향으로 가서 Jackson이 주석을 볼 수 없도록했습니다.

범인은 JacksonAnnotationIntrospector.findCreatorAnnotation 입니다. 주의:

if (_cfgConstructorPropertiesImpliesCreator
            && config.isEnabled(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES)

Also notice JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator:

public JacksonAnnotationIntrospector setConstructorPropertiesImpliesCreator(boolean b)
{
    _cfgConstructorPropertiesImpliesCreator = b;
    return this;
}

So two options, either set the MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES to false or create a JacksonAnnotationIntrospector set setConstructorPropertiesImpliesCreator to false and set this AnnotationIntrospector into the ObjectMapper via ObjectMapper.setAnnotationIntrospector.

Notice a couple things, I am using Jackson 2.8.10 and in that version MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES does not exist. I am not sure in which version of Jackson it was added. So if it is not there, use the JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator mechanism.


I struggled with this for a moment as well. But looking through the documentation here I can see that the onConstructor annotation parameter is considered experimental and is not supported well on my IDE (STS 4). According to the Jackson documentation, private members are not (de)serialized by default. There are quick ways to resolve this.

Add JsonAutoDetect annotation and set it appropriately to detect protected/private members. This is convenient for DTOs

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class SomeClass

Add a factory function with @JsonCreator annotation, this works best if you need some object validation or additional transforms.

public class SomeClass {

   // some code here

   @JsonCreator
   public static SomeClass factory(/* params here dressing them in @JsonProperty annotations*/) {
      return new SomeClass();
   }
}

Of course you could just manually add the constructor in yourself also as well.


From Jan Rieke's Answer

Since lombok 1.18.4, you can configure what annotations are copied to the constructor parameters. Insert this into your lombok.config:

lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty

Then just add @JsonProperty to your fields:

...

You'll need a @JsonProperty on every field even if the name matches, but that is a good practice to follow anyway. You can also set your fields to public final using this, which I prefer over getters.

@ToString
@EqualsAndHashCode
@Wither
@AllArgsConstructor(onConstructor=@__(@JsonCreator))
public class TestFoo {
    @JsonProperty("xoom")
    private final String x;
    @JsonProperty("z")
    private final int z;
}

Try adding a @NoArgsConstructor


I had a different issue and it was with the boolean primitive types.

private boolean isAggregate;

It was throwing the following error as a result

Exception: Unrecognized field "isAggregate" (class 

Lambok converts isAggregate to isAggregate() as a getter making the property internally to lombok as aggregate instead isAggregate. The Jackson library doesn't like it and it needs isAggregate property instead.

I updated the primitive boolean to Wrapper Boolean to work around this issue. There are other options for you if you are dealing with boolean types, see the reference below.

Sol:

private Boolean isAggregate;

ref: https://www.baeldung.com/lombok-getter-boolean

ReferenceURL : https://stackoverflow.com/questions/39381474/cant-make-jackson-and-lombok-work-together

반응형