我有一个来自API的Date格式,如下所示:
"start_time": "2015-10-1 3:00 PM GMT+1:00"
这是YYYY-DD-MM HH:MM am / pm GMT时间戳。我将此值映射到POJO中的Date变量。显然,其显示转换错误。
我想知道两件事:
- 与Jackson进行转换时,我需要使用哪种格式?Date是否适合此字段类型?
- 通常,是否有一种方法可以在Jackson将变量映射到Object成员之前对其进行处理?诸如更改格式,计算等。
我有一个来自API的Date格式,如下所示:
"start_time": "2015-10-1 3:00 PM GMT+1:00"
这是YYYY-DD-MM HH:MM am / pm GMT时间戳。我将此值映射到POJO中的Date变量。显然,其显示转换错误。
我想知道两件事:
Answers:
与Jackson进行转换时,我需要使用哪种格式?Date是否适合此字段类型?
Date
是一个很好的字段类型。您可以使用ObjectMapper.setDateFormat
以下命令轻松地使JSON可解析:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
myObjectMapper.setDateFormat(df);
通常,是否有一种方法可以在Jackson将变量映射到Object成员之前对其进行处理?诸如更改格式,计算等。
是。您有几种选择,包括实现自定义JsonDeserializer
,例如extends JsonDeserializer<Date>
。这是一个好的开始。
LocalDateTime
或ZonedDateTime
代替Date
?由于Date
基本上已弃用(或至少使用了许多方法),因此我想使用这些替代方法。
java.util.Date
明确提及,因此我想指出,这不适用于“ java.sql.Date.
另请参阅下面的答案”。
从Jackson v2.0开始,您可以直接在Object成员上使用@JsonFormat注释;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z")
private Date date;
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone="GMT")
当然,有一种称为序列化和反序列化的自动化方法,您也可以使用pb2q提到的特定注释(@JsonSerialize,@JsonDeserialize)对其进行定义。
您可以同时使用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 ...
}
使用此实现,序列化和反序列化过程的执行将连续产生原始值。
我认为仅使用@JsonFormat注释反序列化会得出不同的结果,因为库内部时区默认设置,您无法使用注释参数进行更改(这也是我对Jackson库2.5.3和2.6.3版本的经验)。
只是具有日期时间格式的Spring Boot应用程序的完整示例RFC3339
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 BaiJiFeiLong@gmail.com 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"));
}
}
以@ 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注释的替代方法,您还可以考虑通过在应用程序级别应用自定义序列化来覆盖Jackson的默认序列化,即Date类型的所有类成员都将由Jackson进行序列化。使用此自定义序列化,而无需在每个字段上进行显式注释。例如,如果您使用的是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;
}
}
如果有人对使用java.sql.Date的自定义日期格式有疑问,这是最简单的解决方案:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(java.sql.Date.class, new DateSerializer());
mapper.registerModule(module);
(此SO-answer为我省去了很多麻烦:https : //stackoverflow.com/a/35212795/3149048)
杰克逊默认将SqlDateSerializer用于java.sql.Date,但是当前,此序列化程序未考虑dateformat,请参阅以下问题:https : //github.com/FasterXML/jackson-databind/issues/1407。解决方法是为代码示例中所示的java.sql.Date注册一个不同的序列化程序。
我想指出的是,设置SimpleDateFormat
其他答案中描述的“顶”仅适用于java.util.Date
我认为是问题所在的顶视图。但是对于java.sql.Date
格式化程序不起作用。在我的情况下,为什么格式化程序不起作用并不十分明显,因为在应该序列化的模型中,字段实际上是a,java.utl.Date
但是实际对象最终变成a 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
将不会添加。
这将是突破性的变化,我认为这不是必须的。如果我们从头开始,我会同意所做的更改,但是事情并没有那么多。
Date
类别的这种不良设计的后果。如今,也不应该是其中任何一个SimpleDateFormat
。近5年前,现代Java日期和时间API java.time取代了它们。使用它和FasterXML / jackson-modules-java8。