Я пытаюсь десериализовать экземпляр этого класса с помощью 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 ()}]
Есть ли способ десериализации класса с перегруженными конструкторами с помощью Джексона?
благодаря
Ответы:
Хотя это не задокументировано должным образом, у вас может быть только один создатель для каждого типа. В вашем типе может быть столько конструкторов, сколько вы хотите, но только один из них должен иметь
@JsonCreator
аннотацию.источник
РЕДАКТИРОВАТЬ : вот, в сообщении в блоге разработчиков Джексона, кажется, что 2.12 может увидеть улучшения в отношении внедрения конструктора. (Текущая версия на момент редактирования - 2.11.1)
Это все еще верно для базы данных Джексона 2.7.0.
Джексон
@JsonCreator
аннотации 2,5 Javadoc или Джексон аннотаций документации грамматик ( конструктор s и завод метод s ) Пусть считает , что на самом деле можно отметить несколько конструкторов.Глядя на код, в котором указаны создатели , похоже, что Джексон
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, вы можете сделать следующее:
@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; } }
Это был мой случай, когда я нашел ваш вопрос. Мне потребовалось некоторое время, чтобы понять, как это решить, возможно, это то, что вы собирались делать. Решение @Brice у меня не сработало.
источник
Если вы не против проделать еще немного работы, вы можете десериализовать объект вручную:
@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"); } } } }
источник