JsonCreator를 사용하여 오버로드 된 생성자로 클래스를 역 직렬화하는 방법


82

Jackson 1.9.10을 사용하여이 클래스의 인스턴스를 역 직렬화하려고합니다.

public class Person {

@JsonCreator
public Person(@JsonProperty("name") String name,
        @JsonProperty("age") int age) {
    // ... person with both name and age
}

@JsonCreator
public Person(@JsonProperty("name") String name) {
    // ... person with just a name
}
}

이것을 시도하면 다음을 얻습니다.

충돌하는 속성 기반 제작자 : 이미 ... {interface org.codehaus.jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator ()}], 만남 ..., 주석 : {interface org.codehaus. jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator ()}]

Jackson을 사용하여 오버로드 된 생성자로 클래스를 역 직렬화하는 방법이 있습니까?

감사


4
대답에서 알 수 있듯이 아니요, 생성자를 하나만 지정해야합니다. 귀하의 경우에는 여러 인수를 취하는 것을 남겨 두십시오. "누락 된"인수는 null (객체의 경우) 또는 기본값 (기본형의 경우)을 사용합니다.
StaxMan 2013

감사. 여러 생성자를 허용하는 것은 좋은 기능입니다. 사실, 내 예는 약간 인위적입니다. 내가 사용하려는 객체는 실제로 완전히 다른 인수 목록을 가지고 있습니다. 하나는 정상적으로 생성되고 다른 하나는 Throwable로 생성됩니다. 의 Throwable
geejay

예, 좋을 거라고 확신하지만 규칙은 서로 다른 순열로 다소 복잡해질 수 있습니다. 새로운 기능, 기능에 대한 RFE를 항상 제출할 수 있습니다.
StaxMan 2013

답변:


118

제대로 문서화되어 있지는 않지만 유형 당 한 명의 작성자 만 가질 수 있습니다. 유형에 원하는만큼 생성자를 가질 수 있지만 그 중 하나에 만 @JsonCreator주석이 있어야 합니다.


69

편집 : Jackson의 관리자가 작성한 블로그 게시물 에서 2.12는 생성자 주입과 관련하여 개선 사항을 볼 수 있습니다. (이 편집 당시의 현재 버전은 2.11.1입니다.)

모호한 1- 인수 생성자 (위임 대 속성)로 문제 해결 / 완화를 포함하여 생성자 생성자의 자동 감지 기능을 개선합니다.


이것은 Jackson databind 2.7.0의 경우에도 마찬가지입니다.

잭슨 @JsonCreator주석 2.5의 javadoc 이나 잭슨 주석 문서의 문법 ( 생성자 공장 방법 ) 사람이 할 수있는 참으로 믿게 표시 여러 생성자를.

생성자 및 팩토리 메서드를 연결된 클래스의 새 인스턴스를 인스턴스화하는 데 사용할 하나로 정의하는 데 사용할 수있는 마커 주석입니다.

생성자 가 식별 되는 코드를 보면 Jackson CreatorCollector생성자 의 첫 번째 인수확인 하기 때문에 오버로드 된 생성자를 무시 하는 것처럼 보입니다 .

Class<?> oldType = oldOne.getRawParameterType(0);
Class<?> newType = newOne.getRawParameterType(0);

if (oldType == newType) {
    throw new IllegalArgumentException("Conflicting "+TYPE_DESCS[typeIndex]
           +" creators: already had explicitly marked "+oldOne+", encountered "+newOne);
}
  • oldOne 처음으로 확인 된 생성자 생성자입니다.
  • newOne 오버로드 된 생성자 생성자입니다.

즉, 그런 코드 가 작동하지 않습니다.

@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
    this.country = "";
}

@JsonCreator
public Phone(@JsonProperty("country") String country, @JsonProperty("value") String value) {
    this.value = value;
    this.country = country;
}

assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336"); // raise error here
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");

그러나이 코드는 작동합니다.

@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
    enabled = true;
}

@JsonCreator
public Phone(@JsonProperty("enabled") Boolean enabled, @JsonProperty("value") String value) {
    this.value = value;
    this.enabled = enabled;
}

assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");

이것은 약간 엉망이고 미래의 증거가 아닐 수도 있습니다 .


문서는 객체 생성 작동 방식에 대해 모호합니다. 하지만 코드에서 수집 한 내용에서 다른 방법을 혼합 할 수 있다는 것입니다.

예를 들어 다음과 같이 주석이 달린 정적 팩토리 메소드를 가질 수 있습니다. @JsonCreator

@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
    enabled = true;
}

@JsonCreator
public Phone(@JsonProperty("enabled") Boolean enabled, @JsonProperty("value") String value) {
    this.value = value;
    this.enabled = enabled;
}

@JsonCreator
public static Phone toPhone(String value) {
    return new Phone(value);
}

assertThat(new ObjectMapper().readValue("\"+336\"", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");

작동하지만 이상적이지 않습니다. 결국 그것은 의미가있을 수 있습니다. 예를 들어 json이 동적 인 경우에는 여러 주석 생성자보다 훨씬 더 우아하게 페이로드 변형을 처리하기 위해 위임 생성자를 사용해야합니다.

또한 Jackson 은 다음 코드와 같이 우선 순위에 따라 제작자를 주문 합니다.

// Simple
@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
}

// more
@JsonCreator
public Phone(Map<String, Object> properties) {
    value = (String) properties.get("value");
    
    // more logic
}

assertThat(new ObjectMapper().readValue("\"+336\"", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");

이 시간 잭슨은 오류가 발생하지 않지만 잭슨에만 사용 위임 생성자 Phone(Map<String, Object> properties)는 그 수단 Phone(@JsonProperty("value") String value)사용되지 않습니다를.


7
이 좋은 예와 자세한 설명을 제공하기 때문에 이럴이 허용 대답을해야한다
matiou

7

달성하려는 것을 올바르게 얻었다면 생성자 오버로드없이 해결할 수 있습니다 .

JSON 또는 맵에없는 속성에 null 값을 입력하려는 경우 다음을 수행 할 수 있습니다.

@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
    private String name;
    private Integer age;
    public static final Integer DEFAULT_AGE = 30;

    @JsonCreator
    public Person(
        @JsonProperty("name") String name,
        @JsonProperty("age") Integer age) 
        throws IllegalArgumentException {
        if(name == null)
            throw new IllegalArgumentException("Parameter name was not informed.");
        this.age = age == null ? DEFAULT_AGE : age;
        this.name = name;
    }
}

내가 당신의 질문을 발견했을 때의 경우였습니다. 그것을 해결하는 방법을 알아내는 데 시간이 좀 걸렸습니다. 아마도 그게 당신이하려는 일이었을 것입니다. @Brice 솔루션 이 나를 위해 작동하지 않았습니다.


1
최고의 anwer 이럴
야콥

3

조금 더 작업해도 괜찮다면 엔터티를 수동으로 역 직렬화 할 수 있습니다.

@JsonDeserialize(using = Person.Deserializer.class)
public class Person {

    public Person(@JsonProperty("name") String name,
            @JsonProperty("age") int age) {
        // ... person with both name and age
    }

    public Person(@JsonProperty("name") String name) {
        // ... person with just a name
    }

    public static class Deserializer extends StdDeserializer<Person> {
        public Deserializer() {
            this(null);
        }

        Deserializer(Class<?> vc) {
            super(vc);
        }

        @Override
        public Person deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            JsonNode node = jp.getCodec().readTree(jp);
            if (node.has("name") && node.has("age")) {
                String name = node.get("name").asText();
                int age = node.get("age").asInt();
                return new Person(name, age);
            } else if (node.has("name")) {
                String name = node.get("name").asText();
                return new Person("name");
            } else {
                throw new RuntimeException("unable to parse");
            }
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.