Spring Boot issues serializing java.time.LocalDateTime with Jackson to return ISO-8601 JSON timestamps? - serialization

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.
UPDATE:
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.

The error occurs because you mix versions of Jackson. You are using version 1.3.6.RELEASE of Spring Boot. If you would migrate to Spring Boot version 2.x.x.RELEASE then you can replace the com.fasterxml.jackson.datatype dependency by a spring-boot-starter-json dependency. In this way you let Spring Boot take care of the correct Jackson version.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>

Related

ActiveJDBC DBSpec - JUnit5 support

For a JavaEE project with working JUnit 5 tests I decided to give ActiveJDBC a try. Unfortunately I implemented all the tests with JUnit 5 before this decision so I have to update my tests for the usage with ActiveJDBC and was wondering why they won't get the test-db connection out of the database.properties file when extending the class with 'DBSpec' and just throw an DBException when using JUnit version 5 instead of version 4:
org.javalite.activejdbc.DBException: Failed to retrieve metadata from DB, connection: 'default' is not available
JUnit 5 - test
import org.javalite.activejdbc.test.DBSpec;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
#RunWith(JUnitPlatform.class)
public class MyModelTest extends DBSpec {
//Test will fail with DBException
#Test
public void one() {
MyModel model = new MyModel();
a(model).shouldBe("valid");
}
}
JUnit 5 - dependencies
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.3.0</version>
<scope>test</scope>
</dependency>
When i switch to JUnit 4, everything works as expected
JUnit 4 - test
import org.javalite.activejdbc.test.DBSpec;
import org.junit.Test;
public class MyModelTest extends DBSpec {
//Test will pass
#Test
public void one() {
MyModel model= new MyModel();
a(model).shouldBe("valid");
}
}
JUnit 4 - dependencies
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
It won't be that much of a deal to just 'rewrite' my tests for JUnit4 but are there any plans to support JUnit5 in the near future?
You are correct, JUnit5 is not supported yet. We filed a new issue: https://github.com/javalite/activejdbc/issues/784 to add support for it. Please, track it to see when the next snapshot will be available.

No ParameterResolver registered for an #RepeatedTest

I have a not parameterized but #RepeatedTest. When running the acceptance test, the failsafe plugin throws ParameterResolutionException (no ParameterResolver registered for parameter) for #BeforeEach method (in the testcase parent class) which accepts RepetitionInfo. The pom.xml has for JUnit:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
As I've understood from the documentation (§3.11, §3.13.1), nothing else is required (RepetitionInfoParameterResolver is registered automatically and RepetitionInfo is available #BeforeEach), but it seems I'm wrong. What should I do?
... and now the code :-)
#Retention(RUNTIME)
#SuppressWarnings("javadoc")
#Target(METHOD)
#RepeatedTest(AcceptanceTestUtil.MAX_WEBBROWSER_COUNT)
#Tag("AcceptanceTest")
#Test // <- bug lies here, conflicts with #RepeatedTest
public #interface AcceptanceTest {
}

Why does jersey jackson/json processing fail on polymorphic types?

I have a simple example program that duplicates what I believe is a bug, but in case there are real experts out there who know better than I do what's going on, I'll post my issue here.
Here is my Main class:
public class Main {
public static final String BASE_URI = "http://0.0.0.0:8080/myapp/";
public static HttpServer startServer() {
final ResourceConfig rc = new ResourceConfig()
.packages("com.example")
.register(EntityFilteringFeature.class)
.register(JacksonFeature.class)
;
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}
public static void main(String[] args) throws IOException {
final HttpServer server = startServer();
System.out.println(String.format("Jersey app started with WADL available at "
+ "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
System.in.read();
server.shutdown();
}
}
And here's my Jersey resource:
#Path("myresource")
#Produces(MediaType.APPLICATION_JSON)
public class MyResource {
public static class InnerDataBase {
}
public static class InnerData extends InnerDataBase {
public String item1 = "item1";
public String item2 = "item2";
}
public static class Data {
public String name = "Got it!";
public InnerDataBase innerData = new InnerData();
}
#GET
public Data getIt() {
return new Data();
}
}
The item of note here is that I'm marshalling a class that contains a field whose concrete instance is a derived class with two fields ("item1" and item2") but whose type is actually that of the base class which has no fields. When I run this server and hit the endpoint, I get this:
{"name":"Got it!","innerData":{}}
If I comment out registration of EITHER the EntityFilteringFeature OR the JacksonFeature, the output becomes (as it should):
{"name":"Got it!","innerData":{"item1":"item1","item2":"item2"}}
Conclusion: It appears that the the Jackson media feature is not quite ready for prime time yet in Jersey 2.26-b09.
Additional Thoughts: When I comment out the JacksonFeature, I presume entity filtering is then done with the default Moxy provider. When I comment out EntityFilteringFeature, I presume then that jackson just takes over and handles marshalling for Jersey automatically.
Here's the dependencies section of my pom:
<properties>
<jersey.version>2.26-b09</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-entity-filtering</artifactId>
</dependency>
<!-- both json modules may be included as they're enabled in the ResourceConfig -->
<dependency> <!-- use this one when using moxy json processing -->
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
<dependency> <!-- use this one when using jackson json processing -->
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
</dependencies>
Your thoughts? Am I missing something important here?

WELD-001408: Unsatisfied dependencies for type ObjectMapper with qualifiers #Default at

I try to inject an ObjectMapper using fasterXML jackson like this:
#Inject
private ObjectMapper objectMapper;
but i have the next error when i try to deploy the war file:
org.jboss.weld.exceptions.DeploymentException: WELD-001408:
Unsatisfied dependencies for type ObjectMapper with qualifiers
#Default
This are my dependencies:
<!-- the core, which includes Streaming API, shared low-level abstractions (but NOT data-binding) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-2-version}</version>
</dependency>
<!-- Just the annotations; use this dependency if you want to attach annotations
to classes without connecting them to the code. -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson-2-version}</version>
</dependency>
<!-- databinding; ObjectMapper, JsonNode and related classes are here -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-2-version}</version>
</dependency>
<!-- jackson-dataformat-yaml: Support for reading and writing YAML-encoded data via Jackson abstractions -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson-2-version}</version>
</dependency>
What i did wrong?
I am making the assumption you need the ObjectMapper to do some configuration on it. For this you can use:
#Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
mapper = new ObjectMapper();
// Do some configuration here
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}

Deserialization issues with Joda Time / Jackson 2 / Spring - Can not instantiate value of type [simple type, class org.joda.time.DateTime]

I'm getting an exception when attempting to deserialize an JSON string which contains date strings to a POJO using Joda.
I'm using Jackson2 with Spring and Robospice.
I'm getting the following exception:
Could not read JSON: Can not instantiate value of type [simple type,
class org.joda.time.DateTime] from String value
('2014-07-25T00:00:00'); no single-String constructor/factory method
Here's the code I have at present:
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter
= new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.getObjectMapper().registerModule(new JodaModule());
msgConverters.add(mappingJackson2HttpMessageConverter);
restTemplate.setMessageConverters(msgConverters);
HttpEntity<?> httpEntity = new HttpEntity<Object>(headers);
final ResponseEntity<HolidayList> responseEntity
= restTemplate.exchange(url, HttpMethod.GET, httpEntity,HolidayList.class);
The POJO fields are defined like so:
private DateTime departureDate;
I had this working in Jackson1... but can't seem to get it working in Jackson2.
For Maven user: This problem occurs when you are using jackson and joda but forget to include jackson-datatype-joda. For latest jackson version as of this answer (2.6.3), following is the dependencies that you have to include in your pom file.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>2.6.3</version>
</dependency>
I believe my issue was simply that I was running different versions of the jackson joda component to everything else.
In the end I did this in my Gradle file
String jacksonCore = 'com.fasterxml.jackson.core:jackson-core:'
String jacksonAnnotations = 'com.fasterxml.jackson.core:jackson-annotations:'
String jacksonDatabind = 'com.fasterxml.jackson.core:jackson-databind:'
String jacksonJoda='com.fasterxml.jackson.datatype:jackson-datatype-joda:'
String jacksonVersion = '2.4.1'
dependencies {
compile jacksonCore + jacksonVersion
compile jacksonAnnotations + jacksonVersion
compile jacksonDatabind + jacksonVersion
compile jacksonJoda + jacksonVersion