itsource

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

mycopycode 2022. 9. 6. 22:31
반응형

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.codehouse.coderate.coderate.JsonCreator@org.codehaus.jackson.an 를 참조해 주세요.JsonCreator()}), ..., 주석: {interface org.codehouse.jackson.annotate.JsonCreator@org.codehaus.jackson.an 를 참조해 주세요.Json Creator()]

Jackson을 사용하여 과부하 컨스트럭터가 있는 클래스를 역직렬화하는 방법이 있습니까?

고마워요.

제대로 문서화되어 있지는 않지만 유형별로 작성자를 하나만 가질 수 있습니다.원하는 수만큼 컨스트럭터를 가질 수 있지만, 그 중 한 개만 컨스트럭터를 가질 수 있습니다.@JsonCreator주석을 달아주세요.

편집: Jackson의 유지보수에 의한 블로그 투고에서는 컨스트럭터 주입에 관해 2.12가 개선될 가능성이 있는 것 같습니다.(이 편집 시점에서의 현재 버전은 2.11.1입니다.

애매한 1인수 생성자(위임 vs 속성)로 문제 해결/해소를 포함한 생성자 자동 검출 향상


이는 Jackson databind 2.7.0에도 해당됩니다.

Jackson 주석 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이 그렇게 동적인 경우 여러 개의 주석이 달린 컨스트럭터보다 훨씬 우아하게 페이로드 변동을 처리하기 위해 위임 컨스트럭터를 사용하는 것이 좋습니다.

또한 잭슨은 다음 코드와 같이 우선순위별로 크리에이터를 주문합니다.

// 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)을 사용하다

당신이 달성하고자 하는 것을 내가 맞췄다면, 당신은 시공자의 과부하 없이 그것을 해결할 수 있습니다.

JSON 또는 Map에 없는 Atribute에 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;
    }
}

당신의 질문을 발견했을 때 그게 제 경우였습니다.어떻게 풀어야 할지 알아내는데 시간이 좀 걸렸어, 아마 그게 네가 애쓰고 있는 거겠지.@브라이스 솔루션은 나에게 효과가 없었다.

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

@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

반응형