I have a simple application with Spring Boot and Jetty. I have a simple controller returning an object which has a Java 8 ZonedDateTime
:
public class Device {
// ...
private ZonedDateTime lastUpdated;
public Device(String id, ZonedDateTime lastUpdated, int course, double latitude, double longitude) {
// ...
this.lastUpdated = lastUpdated;
// ...
}
public ZonedDateTime getLastUpdated() {
return lastUpdated;
}
}
In my RestController
I simply have:
@RequestMapping("/devices/")
public @ResponseBody List<Device> index() {
List<Device> devices = new ArrayList<>();
devices.add(new Device("321421521", ZonedDateTime.now(), 0, 39.89011333, 24.438176666));
return devices;
}
I was expecting the ZonedDateTime
to be formatted according to the ISO format, but instead I am getting a whole JSON dump of the class like this:
"lastUpdated":{"offset":{"totalSeconds":7200,"id":"+02:00","rules":{"fixedOffset":true,"transitionRules":[],"transitions":[]}},"zone":{"id":"Europe/Berlin","rules":{"fixedOffset":false,"transitionRules":[{"month":"MARCH","timeDefinition":"UTC","standardOffset":{"totalSeconds":3600,"id":"+01:00","rules":{"fixedOffset":true,"transitionRules":[],"transitions":[]}},"offsetBefore":{"totalSeconds":3600,"id":"+01:00","rules":{"fixedOffset":true,"transitionRules":[],"transitions":[]}},"offsetAfter":{"totalSeconds":7200,"id":"+02:00", ...
I just have a spring-boot-starter-web
application, using spring-boot-starter-jetty
and excluding spring-boot-starter-tomcat
.
Why is Jackson behaving like this in Spring Boot?
** UPDATE **
For those looking for a full step by step guide how to solve this I found this after asking the question: http://lewandowski.io/2016/02/formatting-java-time-with-spring-boot-using-json/
There is a library jackson-datatype-jsr310 . Try it.
This library covers new datetime API and includes serializers for ZonedDateTime
too.
All you need is just to add JavaTimeModule
:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
UPDATE
To convert datetime to ISO-8601
string you should disable WRITE_DATES_AS_TIMESTAMPS
feature. You can easily do by either overriding ObjectMapper
bean or by using application properties :
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false
If you either don't rely on SpringBoot's auto-configuration feature - you don't provide spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false
property into your configuration file - or for whatever reason you create ObjectMapper
instance manually. You can disable this feature programatically as follows:
ObjectMapper m = new ObjectMapper();
m.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
this is for jackson 2.8.7
The answer was already mentioned above but I think it's missing some info. For those looking to parse Java 8 timestamps in many forms (not just ZonedDateTime). You need a recent version of jackson-datatype-jsr310
in your POM and have the following module registered:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
To test this code
@Test
void testSeliarization() throws IOException {
String expectedJson = "{\"parseDate\":\"2018-12-04T18:47:38.927Z\"}";
MyPojo pojo = new MyPojo(ZonedDateTime.parse("2018-12-04T18:47:38.927Z"));
// serialization
assertThat(objectMapper.writeValueAsString(pojo)).isEqualTo(expectedJson);
// deserialization
assertThat(objectMapper.readValue(expectedJson, MyPojo.class)).isEqualTo(pojo);
}
Note that you can configure your object mapper globally in Spring or dropwizard to achieve this. I have not yet found a clean way to do this as an annotation on a field without registering a custom (de)serializer.
For Jackson 2.10
and above,
parent pom.xml
<!-- https://github.com/FasterXML/jackson-bom -->
<dependencyManagement>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.10.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
module pom.xml
<!-- https://github.com/FasterXML/jackson-modules-java8 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
JsonMapper creation, possibly in your @Configuration
class
@Bean
public JsonMapper jsonMapper() {
return JsonMapper.builder()
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.addModule(new JavaTimeModule())
.build();
}
Further reading:
The setting spring.jackson.serialization.write-dates-as-timestamps=false
in the application.yml
does not help in our project. Most probably since there are additional libraries working with Jackson: Swagger / OpenAPI / OpenAPI Generator.
What has helped is adding this @EventListener
for the RequestMappingHandlerAdapter
to the @SpringBootApplication
class.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
@SpringBootApplication
public class SpringBootInitializer {
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;
public static void main(String[] args) {
SpringApplication.run(SpringBootInitializer.class, args);
}
@EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
// see https://github.com/FasterXML/jackson-modules-java8/issues/11#issuecomment-913199874
// spring.jackson.serialization.write-dates-as-timestamps=false setting does not work in our configuration (probably because of Swagger / OpenAPI / OpenAPI Generator libraries used)
handlerAdapter
.getMessageConverters()
.forEach(c -> {
if (c instanceof MappingJackson2HttpMessageConverter jsonMessageConverter) {
ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
});
}
}
I have found this solution here — https://github.com/FasterXML/jackson-modules-java8/issues/11#issuecomment-913199874 .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.