Java 8 Localdate Jackson Format

Java 8 LocalDate Jackson format

I was never able to get this to work simple using annotations. To get it to work, I created a ContextResolver for ObjectMapper, then I added the JSR310Module (update: now it is JavaTimeModule instead), along with one more caveat, which was the need to set write-date-as-timestamp to false. See more at the documentation for the JSR310 module. Here's an example of what I used.

Dependency

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

Note: One problem I faced with this is that the jackson-annotation version pulled in by another dependency, used version 2.3.2, which cancelled out the 2.4 required by the jsr310. What happened was I got a NoClassDefFound for ObjectIdResolver, which is a 2.4 class. So I just needed to line up the included dependency versions

ContextResolver

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper MAPPER;

public ObjectMapperContextResolver() {
MAPPER = new ObjectMapper();
// Now you should use JavaTimeModule instead
MAPPER.registerModule(new JSR310Module());
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
}

@Override
public ObjectMapper getContext(Class<?> type) {
return MAPPER;
}
}

Resource class

@Path("person")
public class LocalDateResource {

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getPerson() {
Person person = new Person();
person.birthDate = LocalDate.now();
return Response.ok(person).build();
}

@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createPerson(Person person) {
return Response.ok(
DateTimeFormatter.ISO_DATE.format(person.birthDate)).build();
}

public static class Person {
public LocalDate birthDate;
}
}

Test

curl -v http://localhost:8080/api/person

Result: {"birthDate":"2015-03-01"}

curl -v -POST -H "Content-Type:application/json" -d "{\"birthDate\":\"2015-03-01\"}" http://localhost:8080/api/person

Result: 2015-03-01


See also here for JAXB solution.

UPDATE

The JSR310Module is deprecated as of version 2.7 of Jackson. Instead, you should register the module JavaTimeModule. It is still the same dependency.

JSON Java 8 LocalDateTime format in Spring Boot

update: Spring Boot 2.x doesn't require this configuration anymore. I've written a more up to date answer here.


(This is the way of doing it before Spring Boot 2.x, it might be useful for people working on an older version of Spring Boot)

I finally found here how to do it. To fix it, I needed another dependency:

compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.4.0")

By including this dependency, Spring will automatically register a converter for it, as described here. After that, you need to add the following to application.properties:

spring.jackson.serialization.write_dates_as_timestamps=false

This will ensure that a correct converter is used, and dates will be printed in the format of 2016-03-16T13:56:39.492

Annotations are only needed in case you want to change the date format.

How to serialize a Date as LocalDate in Jackson?

An optional part of the format

You may use an optional part in your format pattern string. Such is enclosed in square brackets and denotes a part of the date-time string that may be present or absent during parsing. Like this:

private static final DateTimeFormatter inputFormatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd['T'HH:mm:ssX]");

If you’re sure that appending the irrelevant time and offset (Z is a UTC offset) has not changed the date, you may now parse like this:

    for (String inputString
: new String[] { "2011-12-03T10:15:30Z", "2013-11-21" }) {
LocalDate date = LocalDate.parse(inputString, inputFormatter);
System.out.format("%-20s parsed to %s%n", inputString, date);
}

Output:

2011-12-03T10:15:30Z parsed to 2011-12-03
2013-11-21 parsed to 2013-11-21

How to parse LocalDate with Jackson

Since you set values through contructor, not setters, you should put @JsonFormat(...) on constructor parameters, not fields. This should fix it:

public class LeaveApplUx {

@JsonIgnore
private final String employeeCode;

private final LocalDate startDate;

@JsonIgnore
private final LocalDate rejoinDate;

public LeaveApplUx(@JsonProperty("employeeCode") String employeeCode,
@JsonProperty("startDate") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") LocalDate startDate,
@JsonProperty("rejoinDate") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") LocalDate rejoinDate) {
this.employeeCode = employeeCode;
this.startDate = startDate;
this.rejoinDate = rejoinDate;
}

//getters
}

Deserialize Java 8 LocalDateTime with JacksonMapper

The date time you're passing is not an ISO local date time format.

Change to

@Column(name = "start_date")
@DateTimeFormat(iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME)
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
private LocalDateTime startDate;

and pass the date string in the format '2011-12-03T10:15:30'.

But if you still want to pass your custom format, you just have to specify the right formatter.

Change to

@Column(name = "start_date")
@DateTimeFormat(iso = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
private LocalDateTime startDate;

I think your problem is the @DateTimeFormat has no effect at all. As the Jackson is doing the deserialization and it doesn't know anything about spring annotation, and I don't see spring scanning this annotation in the deserialization context.

Alternatively, you can try setting the formatter while registering the Java time module.

LocalDateTimeDeserializer localDateTimeDeserializer = new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);

Here is the test case with the deseralizer which works fine. Maybe try to get rid of that DateTimeFormat annotation altogether.

@RunWith(JUnit4.class)
public class JacksonLocalDateTimeTest {

private ObjectMapper objectMapper;

@Before
public void init() {
JavaTimeModule module = new JavaTimeModule();
LocalDateTimeDeserializer localDateTimeDeserializer = new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);
objectMapper = Jackson2ObjectMapperBuilder.json()
.modules(module)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.build();
}

@Test
public void test() throws IOException {
final String json = "{ \"date\": \"2016-11-08 12:00\" }";
final JsonType instance = objectMapper.readValue(json, JsonType.class);

assertEquals(LocalDateTime.parse("2016-11-08 12:00",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") ), instance.getDate());
}
}


class JsonType {
private LocalDateTime date;

public LocalDateTime getDate() {
return date;
}

public void setDate(LocalDateTime date) {
this.date = date;
}
}

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

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

jackson json date format convert

You can use custom deserializer.

public class EmployeeBirthDateDeserializer extends StdDeserializer<LocalDate> {

public EmployeeBirthDateDeserializer() {
super(LocalDate.class);
}

@Override
public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException {
JsonNode root = parser.getCodec().readTree(parser);
return LocalDate.of(root.get("year").asInt(), root.get("monthValue").asInt(), root.get("dayOfMonth").asInt());
}
}

Then apply it only to dateOfBirth field in Employee using @JsonDeserialize:

public class Employee {

@JsonDeserialize(using = EmployeeBirthDateDeserializer.class)
private LocalDate dateOfBirth;

//getters and setter
}

Test:

public class Temp {

public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Employee employee = mapper.readValue(ClassLoader.getSystemResourceAsStream("strange-date.json"), Employee.class);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
System.out.println(formatter.format(employee.getDateOfBirth()));
}
}

Prints: 1980-05-04.



Related Topics



Leave a reply



Submit