Как десериализовать класс с перегруженными конструкторами с помощью 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?

Спасибо

Ответ 1

Несмотря на то, что он не документирован должным образом, вы можете иметь только одного создателя для каждого типа. Вы можете иметь столько конструкторов, сколько хотите в своем типе, но только один из них должен содержать аннотацию @JsonCreator.

Ответ 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) никогда не используется.