日期格式映射到 JSON Jackson

我有一个来自 API 的日期格式如下:

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

这是 YYYY-DD-MM HH: MM am/pm GMT 时间戳。 我将这个值映射到 POJO 中的 Date 变量。显然,它的显示转换错误。

我想知道两件事:

  1. 我需要使用什么格式来与 Jackson 进行转换?Date 是一个很好的字段类型吗?
  2. 一般来说,在 Jackson 将变量映射到 Object 成员之前,是否有一种方法可以处理这些变量?比如,改变格式,计算等等。
578524 次浏览

我需要使用什么格式来与 Jackson 进行转换?Date 是一个很好的字段类型吗?

Date是一个很好的字段类型。您可以使用 ObjectMapper.setDateFormat非常容易地解析 JSON:

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

一般来说,在 Jackson 将变量映射到 Object 成员之前,是否有一种方法可以处理这些变量?比如,改变格式,计算等等。

是的。您有一些选择,包括实现一个定制的 JsonDeserializer,例如扩展 JsonDeserializer<Date>这个是一个很好的开始。

从 Jackson v2.0开始,您可以直接对 Object 成员使用@JsonFormat 注释;

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

当然,有一种自动化的方式称为序列化和反序列化,您也可以使用 pb2q 提到的特定注释(@ JsonSerialize@ JsonSerialize)来定义它。

您可以同时使用 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版本的经验)。

基于@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 和@JsonSerialize 注释的替代方法,你也可以考虑通过在应用程序级别应用自定义序列化来覆盖 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-答案省去了我很多麻烦: https://stackoverflow.com/a/35212795/3149048)

Jackson 对 java.sql 默认使用 SqlDateSerializer。日期,但是目前,这个序列化程序没有考虑到日期格式,请参阅这个问题: https://github.com/FasterXML/jackson-databind/issues/1407。 解决方法是为 java.sql.Date 注册一个不同的序列化器,如代码示例所示。

这只是 弹簧靴应用程序的 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"));
}
}

我想指出的是,设置一个 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的原因。

这将是破坏性的改变,我认为这是没有根据的。如果我们从头开始,我会同意这种改变,但事实并非如此。

为我工作 SpringBoot。

 import com.alibaba.fastjson.annotation.JSONField;


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

产出:

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

在约会中添加 T 和 Z 等字符

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
private Date currentTime;

输出

{
"currentTime": "2019-12-11T11:40:49Z"
}

如果我们使用的是 Spring 引导应用程序,那么在应用程序范围的配置中还有一个简单实现的选项,那就是在应用程序属性文件中使用。您可以根据需要自定义格式。

spring.jackson.date-format=yyyy-MM-dd'T'HH:mm:ss.SSS

注意: 如果使用此解决方案,请使用 Spring 依赖注入获取 ObjectMapper 类的引用。 由于某些版本的格式发生了变化,因此有时在升级 Jackson 代码中断库时不使用显式格式的弊端就会出现。