杰克逊前

开始使用Spring 5和Spring Boot 2,通过学习的春天课程:

>>看看这个课程

1.概述

在本教程中,我们将使用Jackson序列化日期。我们将从序列化一个简单的java.util开始。日期Joda-Time和Java 8DateTime

2.序列化日期到时间戳

首先 - 让我们看看如何序列化简单java.util.Date与杰克逊

在下面的例子中,我们将序列化一个"事件,它有一个日期场”eventDate”:

@Test public void whenSerializingDateWithJackson_thenSerializedToTimestamp() throws JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");df.setTimeZone (TimeZone.getTimeZone (UTC));日期日期= df。解析(“01-01-1970”01:00);事件事件=新事件(“party”,日期);ObjectMapper = new ObjectMapper();mapper.writeValueAsString(事件);}

这里重要的是,Jackson默认将Date序列化为时间戳格式(自UTC 1970年1月1日以来的毫秒数)。

的实际输出事件序列化:

{“姓名”:“派对”,“eventdate”:3600000}

3.序列化日期iso - 8601

序列化为这种简洁的时间戳格式不是最优的。现在让我们序列化日期iso - 8601格式:

@Test public void whenSerializingDateToISO8601_thenSerializedToText()抛出JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");df.setTimeZone (TimeZone.getTimeZone (UTC));字符串解析= "01-01-1970 02:30";日期日期= df.parse(pararse);事件事件=新事件(“party”,日期);ObjectMapper = new ObjectMapper();mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);// StdDateFormat是ISO8601 since jackson 2.9 mapper。setDateFormat(新StdDateFormat () .withColonInTimeZone(真));String result = mapper.writeValueAsString(事件); assertThat(result, containsString("1970-01-01T02:30:00.000+00:00")); }

请注意,现在日期的表示方式可读性大大提高了。

4.配置objectmapDateFormat

之前的解决方案仍然缺乏选择表示格式的完全灵活性java.util.Date实例。

现在让我们看一看允许我们这样做的配置设置表示日期的格式:

@Test public void whensettingobjectappperdateformat_therect () throws JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");字符串解析= "20-12-2014 02:30";日期日期= df.parse(pararse);事件事件=新事件(“party”,日期);ObjectMapper = new ObjectMapper();mapper.setDateFormat (df);String result = mapper.writeValueAsString(事件);为了(因此,containsString (toParse));}

请注意,尽管我们现在在日期格式方面更加灵活——我们仍然在整个级别上使用全局配置objectmap

5.使用@JsonFormat格式日期

接下来,让我们看看@JsonFormat注释控制各个类的日期格式不是全局的,而是整个应用程序:

public class Event {public String name;@JsonFormat (shape = JsonFormat.Shape.)STRING, pattern = "dd-MM-yyyy hh:mm:ss")}

现在,让我们测试一下:

@test public void whenusingjsonformatannotationtoformatdate_thencorrect()抛出jsonProcessingException,ParseException {simpleDateFormat DF = New SimpleDateFormat(“DD-MM-YYYY HH:MM:SS”);df.setTimeZone (TimeZone.getTimeZone (UTC));字符串解析= "20-12-2014 02:30:00";日期日期= df.parse(pararse);事件事件=新事件(“party”,日期);ObjectMapper = new ObjectMapper();String result = mapper.writeValueAsString(事件);为了(因此,containsString (toParse));}

6.自定义日期序列化器

接下来 - 要完全控制输出,我们将利用自定义串行程序进行日期:

public class CustomDateSerializer extends StdSerializer {private SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");public CustomDateSerializer(){这个(null);}公共CustomDateSerializer(类t){超级(t);} @Override public void serialize (Date value, JsonGenerator gen, SerializerProvider arg2) throws IOException, JsonProcessingException {gen. writestring (formatter.format(value));}}

接下来,让我们把它用作我们的"eventDate”字段:

public class Event {public String name;@jsonserialize(使用= customdateSerializer.class)公共日期eventDate;}

最后,让我们来测试一下:

@Test public void whenusingcustomdateserializer_threct () throws JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");字符串解析= "20-12-2014 02:30:00";日期日期= df.parse(pararse);事件事件=新事件(“party”,日期);ObjectMapper = new ObjectMapper();String result = mapper.writeValueAsString(事件);为了(因此,containsString (toParse));}

进一步阅读:

如何用杰克逊序列化和反序列化枚举

如何使用Jackson 2将枚举序列化和反序列化为JSON对象。

Jackson -自定义序列化器

使用Jackson 2使用自定义序列化器控制JSON输出。

jackson中的自定义反序列化入门

使用Jackson将自定义JSON映射到任何java实体图,并完全控制反序列化过程。

7.与杰克逊连载joda时间

约会并不总是一个例子java.util.Date;实际上 - 他们越来越多地由其他课堂代表 - 当然,一个共同的一类是DateTimeJoda-Time库的实现。

让我们看看怎么做序列化DateTime与杰克逊

我们将利用jackson-datatype-joda模块,以提供开箱即用的Joda-Time支持:

<依赖> < groupId > com.fasterxml.jackson。datatype jackson- joda 2.9.7 

现在我们可以简单地注册JodaModule和做:

@Test public void whenserializingjodatime_threct () throws JsonProcessingException {DateTime date = new DateTime(2014, 12, 20, 2, 30, DateTimeZone.forID(“欧洲/伦敦”));ObjectMapper = new ObjectMapper();映射器。registerModule(新JodaModule ());mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);String result = mapper.writeValueAsString(日期);为了(因此,containsString (2014 - 12 - 20 t02:30:00.000z));}

8.序列化JodaDateTime使用自定义序列化器

如果我们不想要额外的Joda-Time Jackson依赖项,我们也可以利用它一个自定义的序列化器(类似于前面的例子)DateTime实例序列化的清洁:

public class customdatetimeerializer extends StdSerializer {private static DateTimeFormatter formatter = DateTimeFormat。forPattern(“yyyy-MM-dd HH: mm”);public CustomDateTimeSerializer(){这个(null);}公共CustomDateTimeSerializer(类 t){超级(t);} @Override public void serialize (DateTime value, JsonGenerator gen, SerializerProvider arg2) throws IOException, JsonProcessingException {gen. writestring (formatter.print(value));}}

接下来,让我们把它当成我们的财产eventDate“序列化器:

public class Event {public String name;@JsonSerialize(使用= CustomDateTimeSerializer.class) public DateTime eventDate;}

最后,让我们把所有东西放在一起并进行测试:

@Test public void whenserializingjodatimewithjackson_threct () throws JsonProcessingException {DateTime date = new DateTime(2014, 12, 20, 2, 30);事件事件=新事件(“party”,日期);ObjectMapper = new ObjectMapper();String result = mapper.writeValueAsString(事件);为了(因此,containsString(“2014-12-20”02:30));}

9.序列化Java 8日期与杰克逊

接下来,让我们看看如何序列化Java 8DateTime-在本例中,LocalDateTime- 使用杰克逊。我们可以利用Jackson-DataType-JSR310模块:

<依赖> < groupId > com.fasterxml.jackson。datatype jackson-datatype-jsr310 2.9.7 

现在,我们需要做的就是注册JavaTimeModule(JSR310Module),其余的由Jackson负责:

@Test public void whenserializingjava8date_threct () throws JsonProcessingException {LocalDateTime date = LocalDateTime。(2014, 12, 20, 2, 30);ObjectMapper = new ObjectMapper();映射器。registerModule(新JavaTimeModule ());mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);String result = mapper.writeValueAsString(日期);为了(因此,containsString (2014 - 12 - 20 t02:30));}

10.序列化Java 8日期没有任何额外的依赖

如果你不想要额外的依赖,你总是可以使用一个定制的序列化器来编写Java 8DateTime为JSON:

public class CustomLocalDateTimeSerializer extends StdSerializer {private static DateTimeFormatter formatter = DateTimeFormatter。ofPattern(“yyyy-MM-dd HH: mm”);public CustomLocalDateTimeSerializer(){这个(null);}公共CustomLocalDateTimeSerializer(类 t){超级(t);} @Override public void serialize(LocalDateTime值,JsonGenerator gen, SerializerProvider arg2) throws IOException, JsonProcessingException {gen. writestring (formatter.format(value));}}

接下来,让我们使用序列化器eventDate”字段:

public class Event {public String name;@JsonSerialize(使用= CustomLocalDateTimeSerializer.class)公共LocalDateTime eventDate;}

现在,让我们测试一下:

@test公共void whenserializationjava8datewithcustomserializer_thencorrect()抛出jsonprocessingexception {localDateTime日期= localDateTime.of(2014,12,20,2,30);事件事件=新事件(“party”,日期);ObjectMapper = new ObjectMapper();String result = mapper.writeValueAsString(事件);为了(因此,containsString(“2014-12-20”02:30));}

11.反序列化日期

接下来 - 让我们看看如何反驳一个日期杰克逊。在以下示例中 - 我们将“事件"包含日期的实例:

@Test public void whendeserializingdatewithjackson_thorrect () throws JsonProcessingException, IOException {String json = "{"name":"party","eventDate":"20-12-2014 02:30:00"}";SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");ObjectMapper = new ObjectMapper();mapper.setDateFormat (df);事件事件= mapper.readerFor(Event.class).readValue(json);assertequal(“20-12-2014”02:30:00 df.format (event.eventDate));}

12.反序列化JodaZonedDateTime保留时区

在默认配置中,Jackson调整Joda的时区ZonedDateTime到本地上下文的时区。由于默认情况下,本地上下文的时区没有设置,必须手动配置,Jackson将时区调整为GMT:

@Test public void whenDeserialisingZonedDateTimeWithDefaults_thenNotCorrect() throws IOException {ObjectMapper ObjectMapper = new ObjectMapper();objectMapper.findAndRegisterModules ();objectMapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);ZonedDateTime now = ZonedDateTime.now(ZoneId.of(“欧洲/柏林”));String convert = objectMapper.writeValueAsString(现在);ZonedDateTime restored = objectMapper。readValue(转换、ZonedDateTime.class);system . out。Println ("serialized: " + now);system . out。println("restored: " + restored); assertThat(now, is(restored)); }

这个测试用例将失败并输出:

修复:2017-08-14T11:52:22.071Z[UTC]

幸运的是,有一个快速和简单的修复这个奇怪的默认行为:我们只需要告诉杰克逊,而不是调整时区。

这可以通过向上面的测试用例添加下面的代码行来实现:

objectMapper.disable (DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

请注意,为了保留时区,我们还必须禁用将日期序列化为时间戳的默认行为。

13.自定义日期反序列化器

让我们看看如何使用一个自定义日期反序列化器;我们将为属性编写自定义反序列化器"eventDate”:

public class CustomDateDeserializer extends StdDeserializer {private SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");public CustomDateDeserializer(){这个(null);}公共CustomDateDeserializer(类< ?> vc){超级(vc);} @Override public Date deserialize(JsonParser JsonParser, DeserializationContext context) throws IOException, JsonProcessingException {String Date = JsonParser . gettext ();尝试{返回formatter.parse(日期);} catch (ParseException e){抛出新的RuntimeException(e);}}}

接下来,让我们把它作为eventDate“串并转换器:

public class Event {public String name;@JsonDeserialize(使用= CustomDateDeserializer.class)公共日期eventDate;}

最后,我们来测试一下:

@test public void whendeserializeddateusingcustomdeserializer_thencorrect()抛出jsonprocessingException,ioException {string json =“{”name“:”派对“,”eventdate“:”20-12-2014 02:30:00“}”;SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");ObjectMapper = new ObjectMapper();事件事件= mapper.readerFor(Event.class).readValue(json);assertequal(“20-12-2014”02:30:00 df.format (event.eventDate));}

14.修复InvalidDefinition例外

当创建一个LocalDate实例,我们可能会遇到一个异常:

com.fasterxml.jackson.databind.exc.invaliddefinitionException:无法构建“java.time.localdate`的实例”(没有创建者,如默认构造,存在):没有字符串参数构造函数/工厂方法从字符串值反序列化('2014'-12-20')在[来源:(字符串)“2014-12-20”;线路:1,列:1]

出现此问题的原因是因为JSON没有本地有日期格式,所以表示日期字符串

字符串日期的表示不同于类型的对象LocalDate内存中,因此我们需要一个外部反序列化器来从字符串,以及用于呈现日期的序列化器字符串格式。

这些方法也适用于LocalDateTime-唯一的改变是使用一个等价的类LocalDateTime

14.1。杰克逊的依赖

杰克逊允许我们用几种方法来解决这个问题。首先,我们得确保JSR310依赖在我们的pom.xml:

<依赖项>  com.fasterxml.jackson.datatype   jackson-dataType-jsr310   2.11.0  

14.2。序列化到单一日期对象

以便能够处理LocalDate,我们需要注册JavaTimeModule与我们的objectmap

我们也禁用了这个功能write_dates_as_timestamps.objectmap防止Jackson向JSON输出中添加时间数字:

@Test public void whenserializingjava8dateandreadingvalue_threct () throws IOException {String stringDate = "\"2014-12-20\"";ObjectMapper = new ObjectMapper();映射器。registerModule(新JavaTimeModule ());mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);LocalDate result = mapper。readValue (stringDate LocalDate.class);为了(result.toString (), containsString (" 2014-12-20 "));}

在这里,我们使用了Jackson对序列化和反序列化日期的本地支持。

14.3。在Pojo注释

处理这个问题的另一种方法是使用LocalDateDeserializerJsonFormat实体级的注释:

@JsonSerialize(using = LocalDateSerializer.class) @JsonFormat(shape = JsonFormat.Shape. class) @JsonSerialize(using = LocalDateSerializer.class) @JsonFormat(shape = JsonFormat.Shape. class)STRING, pattern = "dd-MM-yyyy"}

@JsonDeserialize注释用于指定自定义反序列化器来解封送JSON对象。同样的,@JsonSerialize指示封送实体时使用的自定义序列化程序。

另外,标注@JsonFormat允许我们指定将日期值序列化的格式。因此,这个POJO可以用来读取和写入JSON:

@Test public void whenserializingjava8dateandreadingfromentity_thcorrect () throws IOException {String json = "{\"name\":\"party\",\"eventDate\":\"20-12-2014\"}";ObjectMapper = new ObjectMapper();EventWithLocalDate result = mapper。readValue (json, EventWithLocalDate.class);为了(result.getEventDate () .toString (), containsString (" 2014-12-20 "));}

虽然这种方法比使用JavaTimeModule默认情况下,它的可定制性要好得多。

15.结论

在这方面日期文章中,我们看了几个方面Jackson可以帮助封送和解封送一个数据到JSON使用我们可以控制的合理格式。

一如既往,可以找到示例代码在GitHub

杰克逊底部

开始使用Spring 5和Spring Boot 2,通过学习的春天课程:

>>看看这个课程
4注释
最古老的
最新的
内联反馈
查看所有评论
对这篇文章的评论关闭!