2012-09-17 54 views
82

我有一个日期格式的API来是这样的:日期格式映射到JSON杰克逊

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

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

我想知道两件事情:

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

回答

70

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

Date是一个很好的字段类型。您可以通过使用ObjectMapper.setDateFormat使JSON解析,能够很容易地:

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

一般情况下,有没有办法来处理变量,他们被映射由杰克逊对象成员之前?类似的,改变格式,计算等。

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

+2

如果格式还包含AM/PM指定,12小时格式会更好:DateFormat df = new SimpleDateFormat(“yyyy-MM-dd hh:mm a z”); –

+0

尝试了所有这些解决方案,但无法将我的POJO的Date变量存储到Map键值中,也作为Date存储。 我想要这个从Map实例化一个BasicDbObject(MongoDB API),然后将这个变量作为Date(不是Long或String)存储在MongoDB DB集合中。 这甚至可能吗? 谢谢 – RedEagle

+1

使用Java 8'LocalDateTime'或'ZonedDateTime'代替'Date'是否容易?由于'日期'基本上已被弃用(或至少它的许多方法),我想使用这些替代品。 – houcros

3

这真是很好的例子,在类的田间地头注释: http://java.dzone.com/articles/how-serialize-javautildate

+0

如果您尝试在您的应用程序中为java.util.Date序列化设置全局配置,则此解决方案不起作用,也会污染您的域模型。 – realPK

+0

@PK它不是一个全局配置,你必须添加注释到你的域模型的日期字段。 – digz6666

1

现在他们已经对日期处理维基页面: http://wiki.fasterxml.com/JacksonFAQDateHandling

+0

很酷。谢谢,@Sutra。 –

+0

这非常有用。通过使用objMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false)更改对象映射器,可以在应用程序中全局设置它。 //将日期写成一个字符串,例如2015-07-06T20:38:41.960 + 0000 –

+1

链接断掉。截至目前,整个网站都拒绝连接。 – cbmeeks

205

由于杰克逊V2.0,你可以使用对象成员直接使用@JsonFormat注解;

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

如果你想包括时区: '@JsonFormat(shape = JsonFormat.Shape.STRING ,pattern =“yyyy-MM-dd'T'HH:mm:ss.SSS'Z'”,timezone =“GMT”)' – realPK

+0

嗨,哪个jar有这个注释。我正在使用jackson-mapper-asl 1.9.10版本。我没有得到它 –

+1

@Ramki:jackson-annotations> = 2.0 –

37

当然中有自动的方式称为序列和反序列化和可以用作为通过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())); 
     } 
    } 
} 

The 解串器 c小姑娘:

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标注反序列化提供了不同的结果我想是因为图书馆内部时区缺省的设置你不能标注参数的变化(这是我与杰克逊库2.5.3和2.6体验。 3版本)。

+2

昨天我已经downvote为我的答案。我在这个话题上做了很多工作,所以我不明白。我可以从中获得一些反馈意见吗?如果遇到downvote,我将不胜感激。这样我们可以彼此了解更多。 –

+0

很好的答案,谢谢它真的帮助我!小建议 - 考虑将CustomCalendarSerializer和CustomCalendarDeserializer作为静态类放在封闭的父类中。我认为这会让代码变得更好:) – Stuart

+0

@Stuart - 你应该简单地将代码的重组作为另一个答案,或者提出一个编辑。米克洛斯可能没有时间自由去做。 – ocodo

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注解,这是所有类成员类型Date将由Jackson使用此自定义序列化进行序列化,而不在每个字段上进行明确的注释。如果您在使用Spring启动例如一个办法做到这一点将按如下:

@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; 
    } 
} 
+0

我低估了你(我讨厌downvoting,但我不想让人们使用你的答案)。 SimpleDateFormat不是线程安全的。这是2016年(你在2016年回答)。有许多更快和线程安全选项时,您不应该使用SimpleDateFormat。甚至有一个确切的误用你在这里提出的Q/A:http://stackoverflow.com/questions/25680728/json-serializer-and-thread-safety –

+1

@AdamGent感谢您指出这一点。在这种情况下,使用Jackson,ObjectMapper类是线程安全的,因此无关紧要。但是,我确实认为代码可能被复制并用于非线程安全的上下文中。所以我编辑了我的答案,以使SimpleDateFormat线程安全。我也承认有替代品,主要是java.time包。 – Stuart

1

如果任何人有使用自定义的日期格式为java.sql中的问题。迄今为止,这是最简单的解决方案:

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

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