日期格式映射到JSON Jackson

Kev*_*ave 133 java json date pojo jackson

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

"start_time": "2015-10-1 3:00 PM GMT+1:00"
Run Code Online (Sandbox Code Playgroud)

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

我想知道两件事:

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

Oli*_*ain 300

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

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

  • 如果你想包括时区:`@JsonFormat(shape = JsonFormat.Shape.STRING,pattern ="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",timezone ="GMT") (56认同)
  • @Ramki:杰克逊注释>= 2.0 (2认同)
  • 此注释仅在序列化阶段才有效,但在反序列化期间,根本不使用时区和区域设置信息.我已经尝试了timezone ="CET"和时区"Europe/Budapest",其中locale ="hu"但是它们都不起作用并导致日历上的奇怪时间对话.只有具有反序列化的自定义序列化才能根据需要处理时区.以下是您需要使用http://www.baeldung.com/jackson-serialize-dates的完美教程 (2认同)

pb2*_*b2q 113

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

Date这是一个很好的领域类型.您可以使用ObjectMapper.setDateFormat以下命令轻松地使JSON解析:

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

通常,有没有办法在变量被Jackson映射到Object成员之前处理变量?比如,更改格式,计算等.

是.您有几个选项,包括实现自定义JsonDeserializer,例如扩展JsonDeserializer<Date>.是一个好的开始.

  • 如果格式还包括AM/PM指定,则12小时格式更好:DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm a z"); (2认同)

Mik*_*van 46

当然,有一种称为序​​列化和反序列化的自动方式,您可以使用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")
Run Code Online (Sandbox Code Playgroud)

如果需要预测结果,则需要使用自定义序列化器和自定义反序列化器而不是@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()));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

解串器类:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以及上述类的用法:

public class CalendarEntry {

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

    // ... additional things ...
}
Run Code Online (Sandbox Code Playgroud)

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

仅使用@JsonFormat注释反序列化提供了不同的结果,我想是因为图书馆内部时区缺省的设置你不能标注参数的变化(这是我与杰克逊库2.5.3和2.6.3版本的经验,以及).

  • 我昨天得到了回答.我在这个主题上做了很多工作,所以我不明白.我可以从中获得一些反馈吗?如果是downvote,我会很感激.这样我们就可以互相学习更多东西. (3认同)
  • 感谢@AdamGent 您的反馈。我理解并接受你的建议。但在这种特殊情况下,我只是想关注带有区域设置信息的 JsonFormat 注释并没有按照我们期望的方式工作。以及如何解决。 (2认同)

Odw*_*ori 9

在您的日期中添加诸如 T 和 Z 之类的字符

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

输出

{
    "currentTime": "2019-12-11T11:40:49Z"
}
Run Code Online (Sandbox Code Playgroud)


Bai*_*ong 6

只是具有日期时间格式的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"));
    }
}
Run Code Online (Sandbox Code Playgroud)


Stu*_*art 5

基于 @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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @AdamGent 感谢您指出这一点。在这种情况下,使用 Jackson,ObjectMapper 类是线程安全的,所以这并不重要。但是,我确实同意您的观点,即代码可以在非线程安全上下文中复制和使用。因此,我编辑了我的答案,以使对 SimpleDateFormat 线程的访问变得安全。我也承认还有其他选择,主要是 java.time 包。 (3认同)