Как принудительно применять ACCEPT_SINGLE_VALUE_AS_ARRAY в процессе десериализации джексона с помощью аннотации
Есть ли способ использовать аннотацию для свойства List в классе, чтобы использовать ACCEPT_SINGLE_VALUE_AS_ARRAY
в Jackson
? Я использую Spring и получаю следующее исключение
Вложенное исключение - com.fasterxml.jackson.databind.JsonMappingException: невозможно десериализовать экземпляр java.util.ArrayList из токена VALUE_STRING.
Предположим, у меня есть класс, как показано ниже:
public class MyClass {
private List < String > value;
}
И мои структуры JSON, как показано ниже:
Случай 1:
[{"operator": "in", "value": ["Active"], "property": "status"}]
случай 2:
[{"operator": "like", "value": "aba", "property": "desc"}]
Какую аннотацию я должен использовать, чтобы сообщить фреймворку, что я хочу, чтобы эти 2 случая рассматривались одинаково при десериализации.
ОБНОВЛЕНИЕ: я переместил обновления в ответ в этом посте для большей ясности.
Ответы
Ответ 1
Я просто отвечаю на свой вопрос для ясности. Одним из ответов было обновление до более высокой версии, чтобы я мог использовать аннотации. Я не могу этого сделать из-за ограничений зависимости моего проекта.
В результате, на основании ответа Михала Фокса, я решил свою проблему, создав собственный десериализатор. Его как показано ниже:
В моей собственности:
@JsonDeserialize(using = CustomStringDeserializer.class)
private List<String> value;
И мой Deserializer:
public class CustomStringDeserializer extends JsonDeserializer<List<String>>{
@Override
public List<String> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature. ACCEPT_SINGLE_VALUE_AS_ARRAY);
return mapper.readValue(p, List.class);
}
}
Ответ 2
Вы можете использовать @JsonFormat аннотацию,
public class MyClass {
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List < String > value;
}
Чтобы работать с этим, вам нужна версия Джексона min 2.7.0
. Вы также можете использовать другие доступные функции JsonFormat
Для версии 2.6.x
@Autowired private ObjectMapper mapper;
//...
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
- Добавьте этот код в свой
Initializer Class
.
- Или вы можете напрямую настроить
Jackson
в вашем Bean Configuration
Это решит проблему, но будет активировано для каждого процесса deserialization
.
Ответ 3
Я бы создал пользовательский JsonDeserializer
, где я имел бы дело с обоими случаями и комментирует значение класса класса с десериализатором.
десериализатор:
public class StringListDeserializer extends JsonDeserializer<List<String>>{
@Override
public List<String> deserialize(JsonParser parser, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
List<String> ret = new ArrayList<>();
ObjectCodec codec = parser.getCodec();
TreeNode node = codec.readTree(parser);
if (node.isArray()){
for (JsonNode n : (ArrayNode)node){
ret.add(n.asText());
}
} else if (node.isValueNode()){
ret.add( ((JsonNode)node).asText() );
}
return ret;
}
}
MyClass:
public class MyClass {
.
@JsonDeserialize(using = StringListDeserializer.class)
private List<String> value;
.
}
Надеюсь, что это поможет.
BTW: Если есть способ справиться с таким случаем только с аннотацией, я также хочу это знать.
Ответ 4
Если этот проект является проектом Spring, вы можете поместить это свойство в себя application.properties
:
spring.jackson.deserialization.UNWRAP_SINGLE_VALUE_ARRAYS=true
Ответ 5
Лучший способ решить эту проблему при использовании RestTemplate.
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
MappingJackson2HttpMessageConverter jacksonMappingConverter
= new MappingJackson2HttpMessageConverter(objectMapper);
restTemplate.getMessageConverters().add(0, jacksonMappingConverter);
Для анализируемого элемента используйте аннотацию, которая может быть объектом или массивом, как указано ниже
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
public class ParentObject{
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
@JsonProperty("InnerObject")
private List<InnerObject> innerObject;
}
Если вы не хотите добавлять новый маппер в restTemplate, измените существующий для поддержки варианта использования
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
for (HttpMessageConverter<?> httpMessageConverter : messageConverters) {
if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = (MappingJackson2HttpMessageConverter) httpMessageConverter;
mappingJackson2HttpMessageConverter.getObjectMapper()
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
}
}