Как десериализовать из JSON в LocalDate, а также использовать JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS?

88
8

Я включил jackson-datatype-joda, но он не работает с JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS.


Я установил ObjectMapper следующим образом:


ObjectMapper jacksonObjectMapper = new ObjectMapper();
jacksonObjectMapper.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true);
jacksonObjectMapper.registerModule(new JodaModule());

При десериализации (используя com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer), я получаю сообщение об ошибке:


org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Current token (VALUE_STRING) not numeric, can not use numeric value accessors
at [...]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Current token (VALUE_STRING) not numeric, can not use numeric value accessors

Я отлаживал LocalDateDeserializer и выяснял, что он ожидает VALUE_NUMBER_INT - в то время как JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS делает дату строкой.


Когда я отключил эту функцию, все было в порядке.


Есть ли вероятность, что эта функция (или аналогичная) будет работать с Joda Time?

спросил(а) 2021-01-25T13:43:25+03:00 4 месяца, 3 недели назад
1
Решение
99

Модуль

Joda использует com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer для десериализации объектов org.joda.time.LocalDate. Этот класс выглядит следующим образом:


@Override
public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
// [yyyy,mm,dd]
if (jp.isExpectedStartArrayToken()) {
jp.nextToken(); // VALUE_NUMBER_INT
int year = jp.getIntValue();
jp.nextToken(); // VALUE_NUMBER_INT
int month = jp.getIntValue();
jp.nextToken(); // VALUE_NUMBER_INT
int day = jp.getIntValue();
if (jp.nextToken() != JsonToken.END_ARRAY) {
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, "after LocalDate ints");
}
return new LocalDate(year, month, day);
}
switch (jp.getCurrentToken()) {
case VALUE_NUMBER_INT:
return new LocalDate(jp.getLongValue());
case VALUE_STRING:
String str = jp.getText().trim();
if (str.length() == 0) { // [JACKSON-360]
return null;
}
return parser.parseLocalDate(str);
default:
}
throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "expected JSON Array, String or Number");
}

Как вы можете видеть, если LocalDate является массивом, этот десериализатор ожидает ints в этом массиве. Вы можете написать свой собственный десериализатор и расширить эту реализацию. Пример ниже:

class LocalDateWithStringsDeserializer extends LocalDateDeserializer {

private static final long serialVersionUID = 1L;

@Override
public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
// [yyyy,mm,dd] or ["yyyy","mm","dd"]
if (jp.isExpectedStartArrayToken()) {
return parseArray(jp, ctxt);
}

return super.deserialize(jp, ctxt);
}

private LocalDate parseArray(JsonParser jp, DeserializationContext ctxt)
throws JsonParseException, IOException {
int year = getNextValue(jp);
int month = getNextValue(jp);
int day = getNextValue(jp);
if (jp.nextToken() != JsonToken.END_ARRAY) {
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, "after LocalDate ints");
}
return new LocalDate(year, month, day);
}

private int getNextValue(JsonParser jp) throws IOException, JsonParseException {
jp.nextToken();

return new Integer(jp.getText());
}
}


Теперь вы можете установить ссылку над десериализатором и свойством:


@JsonDeserialize(using = LocalDateWithStringsDeserializer.class)
private LocalDate date;

ответил(а) 2021-01-25T13:43:25+03:00 4 месяца, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема