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


91

나는 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의 매우 기본적인 사용법이기 때문에이 문제를 해결하는 방법에 대한 유용한 정보를 더 이상 찾을 수 없다는 것이 이상하게 보입니다. 내가 뭔가를 놓친 것일까?

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

답변:


58

불변하지만 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어노테이션이 있는 빈 빌더 클래스를 제거 할 수 있습니다 .


솔루션에 대한 링크는 환영하지만 답변이없는 경우에도 유용한 지 확인하십시오 . 링크 주변에 컨텍스트를 추가 하여 동료 사용자가 그것이 무엇이고 왜 존재하는지 알 수 있도록 한 다음 페이지에서 가장 관련성이 높은 부분을 인용하십시오. 대상 페이지를 사용할 수없는 경우 다시 연결합니다. 링크에 불과한 답변은 삭제 될 수 있습니다.
geisterfurz007

이 솔루션은 Jackson의 뇌사 생성자 기반 역 직렬화 문제를 해결하는 동안 불변성을 유지합니다 (다중 필드 생성자는 지능적인 작업을 시도하는 대신 각 생성자 인수에 @JsonProperty가 필요함). (나는 시도했다 onConstructor _ = 운이없는 {JsonCreator @})
JeeBee

35

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);
    }
}

4
아닌가 AllArgsConstructor이미의 일부 @Value주석?
Zon

8
명시 적 추가는 주석에 @NoArgsConstructor의해 생성 된 생성자를 재정의 @Value하므로 추가로 추가해야합니다@AllArgsConstructor
Davio

1
내가 찾은 최고의 솔루션-내 의견으로는-Builder 패턴이 없습니다. 감사.
Radouxca

13

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

프로젝트의 루트 디렉토리에 lombok.config 파일을 추가 합니다 (아직 수행하지 않은 경우).

lombok.config

안에 붙여 넣으세요

lombok.anyConstructor.addConstructorProperties=true

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

@Data
@AllArgsConstructor
public class MyPojo {

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

2
그러나 이것은 변경 가능하지 않습니까?
vphilipnyc

8

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

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

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


7
suppressConstructorProperties이제 더 이상 사용되지 않습니다. :-(
lilalinux

3
새 기본값도이므로 문제가되지 않습니다 false.
Michael Piefel 18:32에

4

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


14
이것은 내가 OP 원하는 것이 아니다 가정하는, 변경할 수 클래스를 변경
아 미트 골드 스타 인

2

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


2

Jan Rieke의 답변에서

lombok 1.18.4부터 생성자 매개 변수에 복사 할 주석을 구성 할 수 있습니다. 이것을 당신의 lombok.config:

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

그런 다음 @JsonProperty필드에 추가 하십시오.

...

이름이 일치하더라도 모든 필드에 @JsonProperty가 필요하지만 어쨌든 따라가는 것이 좋습니다. 내가 게터보다 선호하는 이것을 사용하여 필드를 공개 최종으로 설정할 수도 있습니다.

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

그래도 getter (+ setters)에서도 작동합니다.


1
이것은 내 문제를 해결합니다! 정말 고맙습니다! JSON과 POJO 간의 spring-boot-integration 및 직렬화에 갇혀 있고 오류가 발생했습니다. Can not deserialize instance of java.lang.String out of START_OBJECT token... 그리고이 문제가 해결되었습니다! @JsonProperty각 생성자 매개 변수에 대한 주석 이 필요하며 @AllArgsConstructorlombok이이를 계측 할 수 있도록 허용합니다.
Mark

1

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


1

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

@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;
}

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


9
이것은 변경 가능한 클래스입니다.
ŁukaszG

0
@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);

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


0

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

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

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

또한 알 JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator를 :

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

그래서 두 가지 옵션은 설정 중 하나를 MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIESfalse로 또는 생성 JacksonAnnotationIntrospector세트 setConstructorPropertiesImpliesCreator로를 false이 설정 AnnotationIntrospectorObjectMapper를 통해 ObjectMapper.setAnnotationIntrospector .

몇 가지 주목할 점은 Jackson 2.8.10을 사용하고 있으며 해당 버전 MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES에는 존재하지 않습니다. 어떤 버전의 Jackson이 추가되었는지 잘 모르겠습니다. 따라서 그것이 없으면 JacksonAnnotationIntrospector.setConstructorPropertiesImpliesCreator메커니즘을 사용하십시오 .


0

이 모듈도 필요합니다. 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>

0

나는 이것으로 잠시 어려움을 겪었습니다. 그러나 여기에 있는 문서를 살펴보면 onConstructor 주석 매개 변수가 실험적인 것으로 간주되고 내 IDE (STS 4)에서 잘 지원되지 않음을 알 수 있습니다. Jackson 문서에 따르면 개인 멤버는 기본적으로 직렬화되지 않습니다. 이를 해결하는 빠른 방법이 있습니다.

JsonAutoDetect 주석을 추가하고 보호 / 비공개 멤버를 감지하도록 적절하게 설정하십시오. 이것은 DTO에 편리합니다.

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

@JsonCreator 어노테이션으로 팩토리 함수를 추가하십시오. 이는 오브젝트 유효성 검증이나 추가 변환이 필요한 경우 가장 잘 작동합니다.

public class SomeClass {

   // some code here

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

물론 직접 생성자를 직접 추가 할 수도 있습니다.


-1

나는 다른 문제가 있었고 부울 기본 유형과 관련이 있습니다.

private boolean isAggregate;

결과적으로 다음 오류가 발생했습니다.

Exception: Unrecognized field "isAggregate" (class 

Lambok 은 내부적으로 속성을 대신 lombok으로 만드는 getter 로 변환 isAggregate합니다 . Jackson 라이브러리는 그것을 좋아하지 않으며 대신 속성 이 필요 합니다.isAggregate()aggregateisAggregateisAggregate

이 문제를 해결하기 위해 기본 부울을 Wrapper Boolean으로 업데이트했습니다. boolean유형을 다루는 경우 다른 옵션이 있습니다 . 아래 참조를 참조하십시오.

솔:

private Boolean isAggregate;

참조 : https://www.baeldung.com/lombok-getter-boolean


기본 유형으로 isAggregate 대신 집계를 사용해 보셨습니까? 오류도 해결할 수 있다고 생각합니다. 또한이 stackoverflow.com/a/42620096/6332074
Muhammad Waqas Dilawar (
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.