serialize/deserialize java 8 java.time with Jackson JSON mapper

半腔热情 提交于 2019-11-25 18:49:18

There's no need to use custom serializers/deserializers here. Use jackson-modules-java8's datetime module:

Datatype module to make Jackson recognize Java 8 Date & Time API data types (JSR-310).

This module adds support for quite a few classes:

  • Duration
  • Instant
  • LocalDateTime
  • LocalDate
  • LocalTime
  • MonthDay
  • OffsetDateTime
  • OffsetTime
  • Period
  • Year
  • YearMonth
  • ZonedDateTime
  • ZoneId
  • ZoneOffset
Alexander Taylor

Update: Leaving this answer for historical reasons, but I don't recommend it. Please see the accepted answer above.

Tell Jackson to map using your custom [de]serialization classes:

@JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime ignoreUntil; 

provide custom classes:

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {     @Override     public void serialize(LocalDateTime arg0, JsonGenerator arg1, SerializerProvider arg2) throws IOException {         arg1.writeString(arg0.toString());     } }  public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {     @Override     public LocalDateTime deserialize(JsonParser arg0, DeserializationContext arg1) throws IOException {         return LocalDateTime.parse(arg0.getText());     } } 

random fact: if i nest above classes and don't make them static, the error message is weird: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported

greperror

If you are using ObjectMapper class of fasterxml, by default ObjectMapper do not understand the LocalDateTime class, so, you need to add another dependency in your gradle/maven :

compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.3' 

Now you need to register the datatype support offered by this library into you objectmapper object, this can be done by following :

ObjectMapper objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); 

Now, in your jsonString, you can easily put your java.LocalDateTime field as follows :

{     "user_id": 1,     "score": 9,     "date_time": "2016-05-28T17:39:44.937" } 

By doing all this, your Json file to Java object conversion will work fine, you can read the file by following :

objectMapper.readValue(jsonString, new TypeReference<List<User>>() {             }); 
iTake

This maven dependency will solve your problem:

<dependency>     <groupId>com.fasterxml.jackson.datatype</groupId>     <artifactId>jackson-datatype-jsr310</artifactId>     <version>2.6.5</version> </dependency> 

One thing I've struggled is that for ZonedDateTime timezone being changed to GMT during deserialization. Turned out, that by default jackson replaces it with one from context.. To keep zone one must disable this 'feature'

Jackson2ObjectMapperBuilder.json()     .featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) 
Witold Kaczurba

I had a similar problem while using Spring boot. With Spring boot 1.5.1.RELEASE all I had to do is to add dependency:

<dependency>     <groupId>com.fasterxml.jackson.datatype</groupId>     <artifactId>jackson-datatype-jsr310</artifactId> </dependency> 
Sul Aga

If you are using Jersey then you need to add the Maven dependency (jackson-datatype-jsr310) as the others suggested and register your object mapper instance like so:

@Provider public class JacksonObjectMapper implements ContextResolver<ObjectMapper> {    final ObjectMapper defaultObjectMapper;    public JacksonObjectMapper() {     defaultObjectMapper = createDefaultMapper();   }    @Override   public ObjectMapper getContext(Class<?> type) {     return defaultObjectMapper;   }    private static ObjectMapper createDefaultMapper() {     final ObjectMapper mapper = new ObjectMapper();         mapper.registerModule(new JavaTimeModule());     return mapper;   } } 

When registering Jackson in your resources, you need to add this mapper like so:

final ResourceConfig rc = new ResourceConfig().packages("<your package>"); rc   .register(JacksonObjectMapper.class)   .register(JacksonJaxbJsonProvider.class); 

If you can't use jackson-modules-java8 for whatever reasons you can (de-)serialize the instant field as long using @JsonIgnore and @JsonGetter & @JsonSetter:

public class MyBean {      private Instant time = Instant.now();      @JsonIgnore     public Instant getTime() {         return this.time;     }      public void setTime(Instant time) {         this.time = time;     }      @JsonGetter     private long getEpochTime() {         return this.time.toEpochMilli();     }      @JsonSetter     private void setEpochTime(long time) {         this.time = Instant.ofEpochMilli(time);     } } 

Example:

@Test public void testJsonTime() throws Exception {     String json = new ObjectMapper().writeValueAsString(new MyBean());     System.out.println(json);     MyBean myBean = new ObjectMapper().readValue(json, MyBean.class);     System.out.println(myBean.getTime()); } 

yields

{"epochTime":1506432517242} 2017-09-26T13:28:37.242Z 

I use this time format: "{birthDate": "2018-05-24T13:56:13Z}" to deserialize from json into java.time.Instant (see screenshot)

If you are using Spring boot and have this issue with the OffsetDateTime then need to use the registerModules as answered above by @greperror(answered May 28 '16 at 13:04) but note that there is one difference. The dependency mentioned doesn't need to be added as I am guessing that spring boot has it already. I was having this issue with Spring boot and it worked for me without adding this dependency.

Mircea Stanciu

This is just an example how to use it in a unit test that I hacked to debug this issue. The key ingredients are

  • mapper.registerModule(new JavaTimeModule());
  • maven dependency of <artifactId>jackson-datatype-jsr310</artifactId>

Code:

import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.testng.Assert; import org.testng.annotations.Test;  import java.io.IOException; import java.io.Serializable; import java.time.Instant;  class Mumu implements Serializable {     private Instant from;     private String text;      Mumu(Instant from, String text) {         this.from = from;         this.text = text;     }      public Mumu() {     }      public Instant getFrom() {         return from;     }      public String getText() {         return text;     }      @Override     public String toString() {         return "Mumu{" +                 "from=" + from +                 ", text='" + text + '\'' +                 '}';     } } public class Scratch {       @Test     public void JacksonInstant() throws IOException {         ObjectMapper mapper = new ObjectMapper();         mapper.registerModule(new JavaTimeModule());          Mumu before = new Mumu(Instant.now(), "before");         String jsonInString = mapper.writeValueAsString(before);           System.out.println("-- BEFORE --");         System.out.println(before);         System.out.println(jsonInString);          Mumu after = mapper.readValue(jsonInString, Mumu.class);         System.out.println("-- AFTER --");         System.out.println(after);          Assert.assertEquals(after.toString(), before.toString());     }  } 
Janki

You may set this in your application.yml file to resolve Instant time, which is Date API in java8:

spring.jackson.serialization.write-dates-as-timestamps=false 

For those who use Spring Boot 2.x

There is no need to do any of the above - Java 8 LocalDateTime is serialised/de-serialised out of the box. I had to do all of the above in 1.x, but with Boot 2.x, it works seamlessly.

See this reference too JSON Java 8 LocalDateTime format in Spring Boot

If you consider using fastjson, you can solve your problem, note the version

 <dependency>         <groupId>com.alibaba</groupId>         <artifactId>fastjson</artifactId>         <version>1.2.56</version>  </dependency> 

If any one having problem while using SpringBoot here is how I fixed the issue without adding new dependency.

In Spring 2.1.3 Jackson expects date string 2019-05-21T07:37:11.000 in this yyyy-MM-dd HH:mm:ss.SSS format to de-serialize in LocalDateTime. Make sure date string separates the date and time with T not with space. seconds (ss) and milliseconds(SSS) could be ommitted.

@JsonProperty("last_charge_date") public LocalDateTime lastChargeDate; 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!