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