ObjectMapper를 사용하여 기본 생성자없이 불변 객체를 de / serialize하는 방법은 무엇입니까?


106

com.fasterxml.jackson.databind.ObjectMapper를 사용하여 불변 개체를 직렬화 및 역 직렬화하고 싶습니다.

불변 클래스는 다음과 같습니다 (단지 3 개의 내부 속성, 게터 및 생성자).

public final class ImportResultItemImpl implements ImportResultItem {

    private final ImportResultItemType resultType;

    private final String message;

    private final String name;

    public ImportResultItemImpl(String name, ImportResultItemType resultType, String message) {
        super();
        this.resultType = resultType;
        this.message = message;
        this.name = name;
    }

    public ImportResultItemImpl(String name, ImportResultItemType resultType) {
        super();
        this.resultType = resultType;
        this.name = name;
        this.message = null;
    }

    @Override
    public ImportResultItemType getResultType() {
        return this.resultType;
    }

    @Override
    public String getMessage() {
        return this.message;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

그러나이 단위 테스트를 실행할 때 :

@Test
public void testObjectMapper() throws Exception {
    ImportResultItemImpl originalItem = new ImportResultItemImpl("Name1", ImportResultItemType.SUCCESS);
    String serialized = new ObjectMapper().writeValueAsString((ImportResultItemImpl) originalItem);
    System.out.println("serialized: " + serialized);

    //this line will throw exception
    ImportResultItemImpl deserialized = new ObjectMapper().readValue(serialized, ImportResultItemImpl.class);
}

이 예외가 발생합니다.

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class eu.ibacz.pdkd.core.service.importcommon.ImportResultItemImpl]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: {"resultType":"SUCCESS","message":null,"name":"Name1"}; line: 1, column: 2]
    at 
... nothing interesting here

이 예외는 기본 생성자를 만들도록 요청하지만 이것은 변경 불가능한 객체이므로 갖고 싶지 않습니다. 내부 속성을 어떻게 설정합니까? API 사용자를 완전히 혼란스럽게 할 것입니다.

그래서 내 질문은 : 어떻게 든 기본 생성자없이 불변 객체를 역 / 직렬화 할 수 있습니까?


desrializer하는 동안 desrializer는 생성자에 대해 알지 못하므로 기본 생성자에 도달합니다. 이로 인해 불변성을 변경하지 않는 기본 생성자를 만들어야합니다. 또한 수업이 최종적이지 않은데 왜 그렇습니까? 누구든지 기능을 재정의 할 수 있지 않습니까?
Abhinaba Basu

수업이 끝나지 않았습니다. 내가 거기에 쓰는 것을 잊었 기 때문에 알아 주셔서 감사합니다 :)
Michal

답변:


149

Jackson에게 deserialization을위한 개체를 만드는 방법을 알리려면 다음 @JsonCreator@JsonProperty같이 생성자에 대한 및 주석을 사용합니다 .

@JsonCreator
public ImportResultItemImpl(@JsonProperty("name") String name, 
        @JsonProperty("resultType") ImportResultItemType resultType, 
        @JsonProperty("message") String message) {
    super();
    this.resultType = resultType;
    this.message = message;
    this.name = name;
}

1
+1 감사합니다 Sergey. 그것은 작동했다 .. 그러나 나는 JsonCreator 주석을 사용한 후에도 문제가 있었다. 그러나 약간의 검토 후에 나는 나의 자원에서 RequestBody 주석을 사용하는 것을 잊었다는 것을 깨달았다. 이제 작동합니다 .. 고맙습니다.
Rahul

4
기본 생성자를 추가 할 수 없거나 변경할 수있는 POJO에 액세스 할 수 없기 때문에 @JsonCreator@JsonProperty주석을 추가 할 수없는 경우 어떻게 해야합니까? 개체를 역 직렬화하는 방법이 아직 있습니까?
Jack Straw

1
@JackStraw 1) 개체에서 하위 클래스를 만들고 모든 getter 및 setter를 재정의합니다. 2) 클래스에 대한 자체 직렬 변환기 / 역 직렬 변환기를 작성하고 사용합니다 ( "사용자 지정 직렬 변환기"검색 3) 개체를 래핑하고 하나의 속성에 대해서만 직렬 변환기 / 역 직렬 변환기를 작성합니다 (이렇게하면 직렬 변환기를 작성하는 것보다 적은 작업을 수행 할 수 있습니다. 수업 자체이지만 아닐 수도 있습니다).
ErikE

댓글을 +1하여 저를 구했습니다! 지금까지 내가 찾은 모든 솔루션은 기본 생성자를 만드는 것이 었습니다.
Nilson Aguiar

34

개인 기본 생성자를 사용할 수 있으며 Jackson은 개인 최종 생성자 인 경우에도 리플렉션을 통해 필드를 채 웁니다.

편집 : 상속이있는 경우 부모 클래스에 대해 보호 / 패키지 보호 기본 생성자를 사용합니다.


이 답변을 기반으로, deserialise하려는 클래스가 다른 클래스를 확장하는 경우 슈퍼 클래스 (또는 두 클래스)의 기본 생성자를 보호 할 수 있습니다.
KWILLIAMS

3

Sergei Petunin의 첫 번째 대답은 맞습니다. 그러나 생성자의 각 매개 변수에서 중복 @JsonProperty 주석을 제거하여 코드를 단순화 할 수 있습니다.

com.fasterxml.jackson.module.paramnames.ParameterNamesModule을 ObjectMapper에 추가하여 수행 할 수 있습니다.

new ObjectMapper()
        .registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))

(Btw :이 모듈은 기본적으로 SpringBoot에 등록되어 있습니다. JacksonObjectMapperConfiguration에서 ObjectMapper 빈을 사용하거나 Jackson2ObjectMapperBuilder 빈으로 자신 만의 ObjectMapper를 생성하면 모듈의 수동 등록을 건너 뛸 수 있습니다)

예를 들면 :

public class FieldValidationError {
  private final String field;
  private final String error;

  @JsonCreator
  public FieldValidationError(String field,
                              String error) {
    this.field = field;
    this.error = error;
  }

  public String getField() {
    return field;
  }

  public String getError() {
    return error;
  }
}

ObjectMapper는 오류없이이 json을 역 직렬화합니다.

{
  "field": "email",
  "error": "some text"
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.