Есть ли способ десериализации класса с перегруженными конструкторами с помощью Jackson?
Ответ 2
Это все еще сохраняется для файла данных Jackson 2.7.0.
The Jackson @JsonCreator
аннотация 2.5 javadoc или Документация аннотаций Джексона (конструктор s и factory метод s) позволяют полагать, что можно отметить несколько конструкторов.
Аннотации маркера, которые могут использоваться для определения конструкторов и методов factory как один для использования для экземпляров новых экземпляров связанного класса.
Посмотрев на код, в котором идентифицируются создатели, похоже, что 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");
Это немного хакерский и не может быть будущим доказательством.
Документация неясна в отношении того, как работает создание объекта; из того, что я собираю из кода, однако, это то, что можно смешивать разные методы:
Например, можно использовать статический метод factory, аннотированный с помощью @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)
никогда не используется.