Как десериализовать класс с перегруженными конструкторами с помощью 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
}
}
когда я пытаюсь это я получаю следующее
конфликтующие создатели на основе свойств :уже было... {организация интерфейса.codehaus.Джексон.комментировать.JsonCreator @org.codehaus.Джексон.комментировать.JsonCreator()}], встречаются ... , аннотации: {interface org.codehaus.Джексон.комментировать.JsonCreator @org.codehaus.Джексон.комментировать.JsonCreator ()}]
там способ десериализации класса с перегруженными конструкторами с помощью Jackson?
спасибо
4 ответа:
хотя он не задокументирован должным образом, у вас может быть только один создатель для каждого типа. Вы можете иметь столько конструкторов, сколько хотите в своем типе, но только один из них должен иметь
@JsonCreator
аннотация на нем.
это по-прежнему верно для Jackson databind 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 или карте, вы можете сделать следующее:
@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 solution не работает для меня.
Если вы не возражаете делать немного больше работы, вы можете десериализовать объект вручную:
@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"); } } } }