IT TIP

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

itqueen 2020. 10. 28. 21:20
반응형

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


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을 사용하여 오버로드 된 생성자로 클래스를 역 직렬화하는 방법이 있습니까?

감사


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


이것은 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");

This time Jackson won't raise an error, but Jackson will only use the delegate constructor Phone(Map<String, Object> properties), that means the Phone(@JsonProperty("value") String value) is never used.


If I got right what you are trying to achieve, you can solve it without a constructor overload.

If you just want to put null values in the attributes not present in a JSON or a Map you can do the following:

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

That was my case when I found your question. It took me some time to figure out how to solve it, maybe that's what you were tring to do. @Brice solution did not work for me.


If you don't mind doing a little more work, you can deserialize the entity manually:

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

참고URL : https://stackoverflow.com/questions/15931082/how-to-deserialize-a-class-with-overloaded-constructors-using-jsoncreator

반응형

'IT TIP' 카테고리의 다른 글

git stash 및 git pull  (0) 2020.10.28
Freemarker에서 null 값 처리  (0) 2020.10.28
정적 열거 형 대 비 정적 열거 형  (0) 2020.10.28
Firefox가 https로 리디렉션합니다.  (0) 2020.10.28
Bash의 간단한 소켓 서버?  (0) 2020.10.28