I'm working on converting some models in a spring-boot REST API app to use java 8's java.time.LocalDateTime
instead of joda's DateTime
. I want the timestamps returned from API call to adhere to the ISO_8601
format. Motivation is to be forward compatible with Java 8's time (more here).
The part that's proving difficult is when it comes to serialize an object containing LocalDateTime
to JSON.
For example, I have the following entity:
// ... misc imports
import java.time.LocalDateTime;
import java.time.ZoneOffset;
@Data
@Entity
@Table(name = "users")
public class User {
@Id @Column
private String id;
@Column
private String name;
@Column
private String email;
@Column
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "UTC")
private java.time.LocalDateTime createdAt;
public User(String name, String email) {
this.id = Utils.generateUUID();
this.createdAt = LocalDateTime.now(ZoneOffset.UTC);
}
}
I have also set my application.properties
to turn off the dates as timestamp jackson feature:
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false
My maven deps:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.0.1.Final</version>
</dependency>
Finally, I try to retrieve the JSON representation via controller:
@RequestMapping("/users")
@RestController
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@RequestMapping(
value = "/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE
)
public User getUser(@PathVariable("id") String id) {
return userService.findById(id);
}
}
When I actually make a call to this endpoint, I get the following exception:
java.lang.NoSuchMethodError:
com.fasterxml.jackson.datatype.jsr310.ser.JSR310FormattedSerializerBase.findFormatOverrides(Lcom/fasterxml/jackson/databind/SerializerProvider;Lcom/fasterxml/jackson/databind/BeanProperty;Ljava/lang/Class;)Lcom/fasterxml/jackson/annotation/JsonFormat$Value;
Alternately I also configured the app's ObjectMapper
in the configuration class:
@Configuration
public class ServiceConfiguration {
@Bean
public ObjectMapper getJacksonObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(
com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
false
);
return objectMapper;
}
}
Any leads will be greatly appreciated.
Turns out it was a version mismatch between Spring Boot's Jackson version and the one I had in my pom.xml
. As Miloš and Andy proposed, once I've set the correct version and run the app with spring.jackson.serialization.write_dates_as_timestamps=true
, the issue was resolved, without needing to configure the ObjectMapper
or adding annotations on my LocalDateTime
model fields.
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.3.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
</dependencies>
The NoSuchMethodError
is because you are mixing versions of Jackson. Spring Boot 1.3.6 uses Jackson 2.6.7 and you are using 2.8.1 of jackson-datatype-jsr310
.
Spring Boot provides dependency management for Jackson, including jackson-datatype-jsr310
, so you should remove the version from your pom. If you want to use a different version of Jackson, you should override the jackson.version
property:
<properties>
<jackson.version>2.8.1</jackson.version>
</properties>
This will ensure that all your Jackson dependencies have the same version, thereby avoiding problems with mismatched versions.
You can also, if you wish, remove your Java code that's configuring the ObjectMapper
. The Java Time module will be automatically registered when it's in the classpath and writing dates as timestamps can be configured in application.properties
:
spring.jackson.serialization.write-dates-as-timestamps=false
Your ObjectMapper
bean must be marked as @Primary
in order to be picked up by Spring. Alternatively, you can just create a JavaTimeModule
bean and it will get picked up by Spring and added to the default object mapper.
You've probably seen it already but take a look at the official documentation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With