JSON parse error: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value
Solution 1
You need jackson dependency for this serialization and deserialization.
Add this dependency:
Gradle:
compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.4")
Maven:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
After that, You need to tell Jackson ObjectMapper to use JavaTimeModule. To do that, Autowire ObjectMapper in the main class and register JavaTimeModule to it.
import javax.annotation.PostConstruct;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
@SpringBootApplication
public class MockEmployeeApplication {
@Autowired
private ObjectMapper objectMapper;
public static void main(String[] args) {
SpringApplication.run(MockEmployeeApplication.class, args);
}
@PostConstruct
public void setUp() {
objectMapper.registerModule(new JavaTimeModule());
}
}
After that, Your LocalDate and LocalDateTime should be serialized and deserialized correctly.
Solution 2
Spring Boot 2.2.2 / Gradle:
Gradle (build.gradle):
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
Entity (User.class):
LocalDate dateOfBirth;
Code:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
User user = mapper.readValue(json, User.class);
Solution 3
I had a similar issue which I solved by making two changes
- Added below entry in
application.yaml
file
spring:
jackson:
serialization.write_dates_as_timestamps: false
- Add below two annotations to the POJO's
LocalDate
field
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
Example
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
public class Customer {
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
protected LocalDate birthdate;
}
Eample request format:
{"birthdate": "2019-11-28"}
Example request format as array
{"birthdate":[2019,11,18]}
Solution 4
As it turns out, one should not forget to include jacson dependency into the pom file. This solved the issue for me:
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
Solution 5
Well, what I do on every project is a mix of the options above.
First, add the jsr310 dependency:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
Important detail: put this dependency on the top of your depedencies list. I already see a project where the Localdate error persists even with this dependency on the pom.xml. But changing the order of the depedency the error was gone.
On your /src/main/resources/application.yml
file, setup the write-dates-as-timestamps
property:
spring:
jackson:
serialization:
write-dates-as-timestamps: false
And create a ObjectMapper
bean as this:
@Configuration
public class WebConfigurer {
@Bean
@Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.build();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper;
}
}
Following this configuration, the conversion always work on Spring Boot 1.5.x without any error.
Bonus: Spring AMQP Queue configuration
Working with Spring AMQP, pay attention if you have a new instance of Jackson2JsonMessageConverter
(common thing when creating a SimpleRabbitListenerContainerFactory
). You need to pass the ObjectMapper
bean to it, like:
Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter(objectMapper);
Otherwise, you will receive the same error.
Related videos on Youtube
zavanton
Updated on March 16, 2021Comments
-
zavanton about 3 years
I am new to Spring Data REST project and I am trying to create my first RESTful service. The task is simple, but I am stuck.
I want to perform CRUD operations on a user data stored in an embedded database using RESTful API.
But I cannot figure out how to make the Spring framework process the birthData as "1999-12-15" and store it as a LocalDate. The @JsonFormat annotation does not help.
At present I get the error:
HTTP/1.1 400 Content-Type: application/hal+json;charset=UTF-8 Transfer-Encoding: chunked Date: Thu, 24 Aug 2017 13:36:51 GMT Connection: close {"cause":{"cause":null,"message":"Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value ('1999-10-10')\n at [Source: org.apache.catalina.connector.CoyoteInputStream@4ee2a60e; line: 1, column: 65] (through reference chain: ru.zavanton.entities.User[\"birthDate\"])"}, "message":"JSON parse error: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value ('1999-10-10'); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value ('1999-10-10')\n at [Source: org.apache.catalina.connector.CoyoteInputStream@4ee2a60e; line: 1, column: 65] (through reference chain: ru.zavanton.entities.User[\"birthDate\"])"}
How to make it work, so that client calls like:
curl -i -X POST -H "Content-Type:application/json" -d "{ \"firstName\" : \"John\", \"lastName\" : \"Johnson\", \"birthDate\" : \"1999-10-10\", \"email\" : \"[email protected]\" }" http://localhost:8080/users
will actually store the entity into the database.
Below is the information about the classes.
The user class:
package ru.zavanton.entities; import com.fasterxml.jackson.annotation.JsonFormat; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import java.time.LocalDate; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; private String firstName; private String lastName; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private LocalDate birthDate; private String email; private String password; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public LocalDate getBirthDate() { return birthDate; } public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
The UserRepository class:
package ru.zavanton.repositories; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import ru.zavanton.entities.User; @RepositoryRestResource(collectionResourceRel = "users", path = "users") public interface UserRepository extends PagingAndSortingRepository<User, Long> { User findByEmail(@Param("email") String email); }
Application class:
package ru.zavanton; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
-
Juan Carlos Mendoza almost 7 yearsPossible duplicate of serialize/deserialize java 8 java.time with Jackson JSON mapper
-
Cepr0 almost 7 yearsAdd github.com/FasterXML/jackson-modules-java8 as dependency to your pom.xml
-
zavanton almost 7 yearsThanks, @Cepr0! I added the dependency to the pom file and it worked like a charm!
-
-
coretechie over 6 yearsWhat if even after adding these dependencies it is not working. Is there any way I can ask Json to ignore this param or something like that?
-
Tatha over 5 yearsI just had to add the gradle dependency to fix it
-
0cnLaroche over 4 yearsThat part made it work for me
objectMapper.registerModule(new JavaTimeModule());
-
Felipe Desiderati over 4 yearsThere's no need to register the JavaTimeModule. It will be registered automatically if you have the dependency inside the Classpath.
-
Yoga Gowda about 4 yearsIt looks like not registering automatically, I had to register explicitly to work.
-
Dhwanil Patel about 4 yearsThanks, I tried lot many solution but that are not working, but this one is best. How can i study and get more detail for the functionality of this class?
-
sparkyspider about 4 yearsThe yyyy-mm-dd format is standardized under ISO 8601. JSR-310 specifies additions to the Java 8 Date and time API to incorporate ISO8601. The dependency shown above is a patch to add this functionality to Jackson versions prior to 2.85. Version 2.85 and onward support the Java Date and Time API. More info here: github.com/FasterXML/jackson-modules-java8/tree/master/datetime
-
DaddyMoe over 3 yearsIn addition to this correct answer note also, you can have the modules auto-discovered as suppose to manually register them. eg:
new ObjectMapper().findAndRegisterModules();
That said, you should not really mix manual and auto registration if you do, only one of the registrations will have effect. Source: FasterXML jackson-modules-java8 -
DaddyMoe over 3 yearsYou can also have the modules auto-discovered as suppose to explicitly registering them. eg:
new ObjectMapper().findAndRegisterModules();
That said, you should not really mix manual and auto registration if you do, only one of the registrations will have effect. Source: FasterXML jackson-modules-java8 -
farahm over 3 yearsIm still getting the error. In my REST endpoint I am doing:
Registration registration = mapper.convertValue(map.get("person"), Registration.class);
What am I doing wrong? -
Orkhan Hasanli over 3 yearsThank you so much! You saved my time! I also added this line of code to pojo @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
-
CN1002 over 3 yearsThis one looks clean to me.
-
Ventura over 3 yearsThank you, as said above, simple and easy, works great
-
xlm about 3 yearsFWIW I found that modifying the
application.yaml
was not needed but it was necessary to add dependencyjackson-datatype-jsr310
and also include@JsonFormat
as suggested -
Katia Savina over 2 yearsI've just registered the
JavaTimeModule
without adding the dependency, and it works! Had some Jackson dependencies though..