日期格式映射到JSON Jackson

时间:2012-09-17 16:03:01

标签: java json date jackson pojo

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

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

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

我想知道两件事:

  1. 我需要使用什么格式来与杰克逊进行转换? Date是一个很好的字段类型吗?
  2. 一般来说,有没有办法在变量被Jackson映射到Object成员之前处理它们?更改格式,计算等等。

9 个答案:

答案 0 :(得分:275)

自Jackson v2.0起,您可以直接在Object成员上使用@JsonFormat注释;

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

答案 1 :(得分:98)

  

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

Date是一个很好的字段类型。您可以使用ObjectMapper.setDateFormat

轻松地使JSON易于解析
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
myObjectMapper.setDateFormat(df);
  

一般来说,有没有办法在变量被Jackson映射到Object成员之前处理它们?更改格式,计算等等。

是。您有几个选项,包括实施自定义JsonDeserializer,例如延长JsonDeserializer<Date>This是一个好的开始。

答案 2 :(得分:44)

当然有一种称为序列化和反序列化的自动方式,您可以使用特定注释定义它( @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 ...
}

使用此实现,连续执行序列化和反序列化过程会产生原始值。

只使用@JsonFormat注释反序列化会得到不同的结果我认为因为库内部时区默认设置你不能用注释参数改变(这是我对Jackson库2.5.3的体验)和2.6.3版本)。

答案 3 :(得分:3)

使用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"));
    }
}

答案 4 :(得分:1)

在@ 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的默认序列化,即所有类成员类型日期将由杰克逊使用此自定义序列化序列化,而不在每个字段上显式注释。例如,如果您使用的是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;
    }
}

答案 5 :(得分:1)

如果有人在使用java.sql.Date的自定义日期格式时遇到问题,这是最简单的解决方案:

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

Jackson默认情况下对java.sql.Date使用SqlDateSerializer,但是目前,此序列化程序不考虑dateformat,请参阅此问题:https://github.com/FasterXML/jackson-databind/issues/1407。 解决方法是为java.sql.Date注册不同的序列化程序,如代码示例所示。

答案 6 :(得分:1)

我想指出的是,像其他答案中所述设置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

Here还提到了为什么不添加处理java.sql.Date的原因。

  

这将是突破性的变化,我认为这不是必须的。如果我们从头开始,我会同意所做的更改,但是事情不会那么多。

答案 7 :(得分:1)

为我工作。 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"
}

答案 8 :(得分:0)

要在日期中添加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"
}