Отображение формата даты в JSON Jackson
У меня есть формат даты, поступающий из API следующим образом:
"start_time": "2015-10-1 3:00 PM GMT+1:00"
который является гггг-ДД-ММ чч: мм am / pm GMT метка времени.
Я сопоставляю это значение переменной даты в POJO. Очевидно, его показывая ошибку преобразования.
Я хотел бы знать 2 вещи:
- какое форматирование мне нужно использовать для выполнения преобразования с Джексоном? Является ли дата хорошим типом поля для этого?
- в общем, есть ли способ обработать переменные, прежде чем они получат сопоставлено с членами объекта Джексоном? Что-то вроде, изменение формата, расчеты и т. д.
6 ответов:
какое форматирование мне нужно использовать для выполнения преобразования с Джексоном? Является ли дата хорошим типом поля для этого?
Dateявляется прекрасным типом поля для этого. Вы можете сделать JSON parse-able довольно легко с помощьюObjectMapper.setDateFormat:DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z"); myObjectMapper.setDateFormat(df);в общем, есть ли способ обработать переменные, прежде чем они будут сопоставлены с членами объекта Джексоном? Что-то вроде, изменение формата, расчеты и т. д.
да. Вы есть несколько вариантов, в том числе реализации пользовательского
JsonDeserializer, например, расширяяJsonDeserializer<Date>. этой - хорошее начало.
начиная с Jackson v2. 0, вы можете использовать аннотацию @JsonFormat непосредственно на элементах объекта;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z") private Date date;
конечно есть автоматизированный способ называется сериализация и десериализация, и вы можете определить его с помощью конкретных аннотаций (@JsonSerialize, @JsonDeserialize), как упоминалось pb2q, а также.
вы можете использовать оба java.утиль.Дата и java.утиль.Календарь ... и, наверное, JodaTime, а также.
аннотации @JsonFormat не работали для меня, как я хотел (это поправил пояс to другое значение) во время десериализации (сериализация работала идеально):
@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
есть примеры для дата поля, но мне нужно для календарь поля так вот моя реализация:
The сериализатор класс:
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())); } } }The десериализатор класс:
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 ... }С помощью этой реализации выполнение процесса сериализации и десериализации последовательно приводит к исходному значению.
только с помощью аннотации @JsonFormat десериализация дает другой результат Я думаю, что из-за настройки внутреннего часового пояса библиотеки по умолчанию вы не можете изменить параметры аннотации (это был мой опыт работы с библиотекой Джексона 2.5.3 и 2.6.3 версии).
основываясь на очень полезном ответе @ miklov-kriven, я надеюсь, что эти два дополнительных пункта рассмотрения окажутся полезными для кого-то:
(1) я считаю хорошей идеей включить сериализатор и де-сериализатор в качестве статических внутренних классов в одном классе. Нью-Брунсвик, с помощью 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; } }
Если у кого-то есть проблемы с использованием пользовательского dateformat для java.язык SQL.Дата, это самое простое решение:
ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(java.sql.Date.class, new DateSerializer()); mapper.registerModule(module);(этот так-ответ спас меня от многих неприятностей:https://stackoverflow.com/a/35212795/3149048)
Джексон использует SqlDateSerializer по умолчанию для java.язык SQL.Дата, но в настоящее время этот сериализатор не учитывает dateformat, см. Эту проблему:https://github.com/FasterXML/jackson-databind/issues/1407 . Этот обходной путь-зарегистрировать другой сериализатор для java.язык SQL.Дата как показано в примере кода.
просто полный пример для spring boot приложения
RFC3339формат datetimepackage 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")); } }
Comments