首页 文章

日期格式映射到JSON Jackson

提问于
浏览
103

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

"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成员之前处理它们?改变格式,计算等等 .

7 回答

  • 42

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

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z")
    private Date date;
    
  • 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的默认序列化,即所有类型为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;
        }
    }
    
  • 86

    只是 spring boot 应用程序的完整示例,其中包含 RFC3339 datetime格式

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

    因此,如果您想知道为什么您的日期字段格式不正确,请确保该对象确实是 java.util.Date .

    Here也提到了为什么不添加处理 java.sql.Date .

    这将是一个突破性变化,我不认为这是有道理的 . 如果我们从头开始,我会同意这一变化,但事情并非如此 .

  • 243

    当然 there is an automated way 称为序列化和反序列化,您可以使用pb2q提到的特定注释( @JsonSerialize@JsonDeserialize )来定义它 .

    您可以同时使用java.util.Date和java.util.Calendar ...以及JodaTime .

    The @JsonFormat annotations not worked for me as I wanted (它有 adjusted the timezone 到不同的值) during deserialization (序列化工作完美):

    @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")
    

    You need to use custom serializer and custom deserializer instead of the @JsonFormat annotation if you want predicted result. 我在这里找到了真正的好教程和解决方案http://www.baeldung.com/jackson-serialize-dates

    Date 字段的示例,但我需要 Calendar 字段,所以 here is my implementation

    serializer 类:

    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()));
            }
        }
    }
    

    deserializer 班:

    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);
            }
        }
    }
    

    and the usage 以上课程:

    public class CalendarEntry {
    
        @JsonSerialize(using = CustomCalendarSerializer.class)
        @JsonDeserialize(using = CustomCalendarDeserializer.class)
        private Calendar calendar;
    
        // ... additional things ...
    }
    

    Using this implementation the execution of the serialization and deserialization process consecutively results the origin value.

    Only using the @JsonFormat annotation the deserialization gives different result 我认为由于库内部时区默认设置你不能用注释参数改变(这是我对Jackson库2.5.3和2.6.3版本的经验) .

  • 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默认使用SqlDateSerializer作为java.sql.Date,但是目前,此序列化程序不考虑dateformat,请参阅此问题:https://github.com/FasterXML/jackson-databind/issues/1407 . 解决方法是为java.sql.Date注册不同的序列化程序,如代码示例所示 .

  • 0

    我需要使用什么格式来与 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> . This是一个好的开始 .

相关问题