it-swarm.com.ru

Формат даты Отображение в JSON Джексон

У меня есть формат даты из API, например:

"start_time": "2015-10-1 3:00 PM GMT+1:00"

Это ГГГГ-ДД-ММ ЧЧ: ММ утра/вечера по Гринвичу. Я сопоставляю это значение с переменной Date в POJO. Очевидно, что он показывает ошибку преобразования.

Я хотел бы знать 2 вещи:

  1. Какое форматирование мне нужно использовать для конвертации с Джексоном? Является ли Date хорошим типом поля для этого?
  2. В общем, есть ли способ обработать переменные до того, как Джексон отобразит их в члены Object? Что-то вроде смены формата, расчетов и т.д.
121
Kevin Rave

Какое форматирование мне нужно использовать для конвертации с Джексоном? Является ли Date хорошим типом поля для этого?

Date - хороший тип поля для этого. Вы можете сделать JSON-анализ достаточно легко, используя ObjectMapper.setDateFormat:

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
myObjectMapper.setDateFormat(df);

В общем, есть ли способ обработать переменные до того, как Джексон отобразит их в члены Object? Что-то вроде смены формата, расчетов и т.д.

Да. У вас есть несколько вариантов, включая реализацию собственного JsonDeserializer, например расширение JsonDeserializer<Date>. Это это хорошее начало.

101
pb2q

Начиная с Jackson v2.0, вы можете использовать аннотацию @JsonFormat непосредственно для членов Object;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z")
private Date date;
280
Olivier Lecrivain

Конечно существует автоматизированный способ , называемый сериализацией и десериализацией, и вы можете определить его с помощью конкретных аннотаций ( @ JsonSerialize , @ JsonDeserialize ), как уже упоминалось в pb2q.

Вы можете использовать как Java.util.Date, так и Java.util.Calendar ... и, возможно, также JodaTime.

Аннотации @JsonFormat не работают для меня так, как я хотел (он настроил часовой пояс к другому значению) во время десериализации (сериализация работала отлично):

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "CET")

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Europe/Budapest")

Вам нужно использовать пользовательский сериализатор и пользовательский десериализатор вместо аннотации @JsonFormat, если вы хотите получить прогнозируемый результат. Я нашел здесь действительно хорошее руководство и решение - http://www.baeldung.com/jackson-serialize-dates

Существуют примеры для полей даты , но мне нужны поля календаря , поэтому вот моя реализация :

Класс :

public class CustomCalendarSerializer extends JsonSerializer<Calendar> {

    public static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    public static final Locale LOCALE_HUNGARIAN = new Locale("hu", "HU");
    public static final TimeZone LOCAL_TIME_ZONE = TimeZone.getTimeZone("Europe/Budapest");

    @Override
    public void serialize(Calendar value, JsonGenerator gen, SerializerProvider arg2)
            throws IOException, JsonProcessingException {
        if (value == null) {
            gen.writeNull();
        } else {
            gen.writeString(FORMATTER.format(value.getTime()));
        }
    }
}

Класс десериализатора :

public class CustomCalendarDeserializer extends JsonDeserializer<Calendar> {

    @Override
    public Calendar deserialize(JsonParser jsonparser, DeserializationContext context)
            throws IOException, JsonProcessingException {
        String dateAsString = jsonparser.getText();
        try {
            Date date = CustomCalendarSerializer.FORMATTER.parse(dateAsString);
            Calendar calendar = Calendar.getInstance(
                CustomCalendarSerializer.LOCAL_TIME_ZONE, 
                CustomCalendarSerializer.LOCALE_HUNGARIAN
            );
            calendar.setTime(date);
            return calendar;
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

и использование вышеуказанных классов:

public class CalendarEntry {

    @JsonSerialize(using = CustomCalendarSerializer.class)
    @JsonDeserialize(using = CustomCalendarDeserializer.class)
    private Calendar calendar;

    // ... additional things ...
}

Используя эту реализацию, выполнение процесса сериализации и десериализации последовательно приводит к значению Origin.

Только при использовании аннотации @JsonFormat десериализация дает другой результат Я думаю, что из-за настройки по умолчанию внутреннего часового пояса библиотеки вы не можете изменить параметры аннотации (это было мой опыт работы с библиотекой Джексона версии 2.5.3 и 2.6.3).

44
Miklos Krivan

Просто полный пример приложения весенняя загрузка с форматом RFC3339 datetime

package bj.demo;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;

import Java.text.SimpleDateFormat;

/**
 * Created by [email protected] at 2018/5/4 10:22
 */
@SpringBootApplication
public class BarApp implements ApplicationListener<ApplicationReadyEvent> {

    public static void main(String[] args) {
        SpringApplication.run(BarApp.class, args);
    }

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"));
    }
}
3
BaiJiFeiLong

Основываясь на очень полезном ответе @ miklov-kriven, я надеюсь, что эти два дополнительных соображения окажутся полезными для кого-то:

(1) Я считаю хорошей идеей включить сериализатор и десериализатор в качестве статических внутренних классов в одном классе. NB, используя ThreadLocal для безопасности потоков SimpleDateFormat.

public class DateConverter {

    private static final ThreadLocal<SimpleDateFormat> sdf = 
        ThreadLocal.<SimpleDateFormat>withInitial(
                () -> {return new SimpleDateFormat("yyyy-MM-dd HH:mm a z");});

    public static class Serialize extends JsonSerializer<Date> {
        @Override
        public void serialize(Date value, JsonGenerator jgen SerializerProvider provider) throws Exception {
            if (value == null) {
                jgen.writeNull();
            }
            else {
                jgen.writeString(sdf.get().format(value));
            }
        }
    }

    public static class Deserialize extends JsonDeserializer<Date> {
        @Overrride
        public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws Exception {
            String dateAsString = jp.getText();
            try {
                if (Strings.isNullOrEmpty(dateAsString)) {
                    return null;
                }
                else {
                    return new Date(sdf.get().parse(dateAsString).getTime());
                }
            }
            catch (ParseException pe) {
                throw new RuntimeException(pe);
            }
        }
    }
}

(2) В качестве альтернативы использованию аннотаций @JsonSerialize и @JsonDeserialize для каждого отдельного члена класса вы также можете рассмотреть возможность переопределения сериализации по умолчанию Джексона, применяя пользовательскую сериализацию на уровне приложения, то есть все члены класса типа Date будут сериализованы Джексоном. используя эту пользовательскую сериализацию без явной аннотации для каждого поля. Если вы используете Spring Boot, например, один из способов сделать это будет следующим:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public Module customModule() {
        SimpleModule module = new SimpleModule();
        module.addSerializer(Date.class, new DateConverter.Serialize());
        module.addDeserializer(Date.class, new Dateconverter.Deserialize());
        return module;
    }
}
1
Stuart

Если у кого-то есть проблемы с использованием пользовательского формата даты для Java.sql.Date, это самое простое решение:

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Java.sql.Date.class, new DateSerializer());
mapper.registerModule(module);

(Этот SO-ответ избавил меня от многих проблем: https://stackoverflow.com/a/35212795/3149048 )

Джексон использует SqlDateSerializer по умолчанию для Java.sql.Date, но в настоящее время этот сериализатор не учитывает формат даты, смотрите эту проблему: https://github.com/FasterXML/jackson-databind/issues/1407 . Обходной путь должен зарегистрировать другой сериализатор для Java.sql.Date, как показано в примере кода.

1
Stephanie

Я хочу отметить, что установка SimpleDateFormat, как описано в другом ответе, работает только для Java.util.Date, который, как я предполагаю, подразумевается в этом вопросе. Но для Java.sql.Date форматер не работает. В моем случае было не очень очевидно, почему средство форматирования не работало, потому что в модели, которая должна быть сериализована, поле фактически было Java.utl.Date, но фактический объект в конечном итоге был Java.sql.Date. Это возможно, потому что

public class Java.sql extends Java.util.Date

Так что на самом деле это действительно

Java.util.Date date = new Java.sql.Date(1542381115815L);

Поэтому, если вам интересно, почему ваше поле Date неправильно отформатировано, убедитесь, что объект действительно является Java.util.Date.

Здесь также упоминается, почему обработка Java.sql.Date не будет добавлена.

Тогда это приведет к серьезным переменам, и я не думаю, что это оправдано. Если бы мы начинали с нуля, я бы согласился с изменением, но так как все не так уж и много.

1
LazerBass

Работаю на меня. Весенний ботинок.

 import com.alibaba.fastjson.annotation.JSONField;

 @JSONField(format = "yyyy-MM-dd HH:mm:ss")  
 private Date createTime;

Результат:

{ 
   "createTime": "2019-06-14 13:07:21"
}
0
anson