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

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 사용자를 완전히 혼란스럽게 할 것입니다.

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



답변

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


답변

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

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


답변

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"
}


답변