Java 8 LocalDate Jackson format

前端 未结 14 1047
傲寒
傲寒 2020-11-22 12:59

For java.util.Date when I do

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = \"dd/MM/yyyy\")  
  private Date dateOfBirth;
<         


        
相关标签:
14条回答
  • 2020-11-22 13:27

    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.

    0 讨论(0)
  • 2020-11-22 13:27

    As of 2020 and Jackson 2.10.1 there's no need for any special code, it's just a matter of telling Jackson what you want:

    ObjectMapper objectMapper = new ObjectMapper();
    
    // Register module that knows how to serialize java.time objects
    // Provided by jackson-datatype-jsr310
    objectMapper.registerModule(new JavaTimeModule());
    
    // Ask Jackson to serialize dates as String (ISO-8601 by default)
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    

    This has already been mentioned in this answer, I'm adding a unit test verifying the functionality:

    import com.fasterxml.jackson.annotation.JsonFormat;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
    import lombok.Data;
    import org.junit.jupiter.api.Test;
    
    import java.time.LocalDate;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    
    public class LocalDateSerializationTest {
    
        @Data
        static class TestBean {
            // Accept default ISO-8601 format
            LocalDate birthDate;
            // Use custom format
            @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
            LocalDate birthDateWithCustomFormat;
        }
    
        @Test
        void serializeDeserializeTest() throws JsonProcessingException {
            ObjectMapper objectMapper = new ObjectMapper();
    
            // Register module that knows how to serialize java.time objects
            objectMapper.registerModule(new JavaTimeModule());
    
            // Ask Jackson to serialize dates as String (ISO-8601 by default)
            objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    
            // The JSON string after serialization
            String json = "{\"birthDate\":\"2000-01-02\",\"birthDateWithCustomFormat\":\"03/02/2001\"}";
    
            // The object after deserialization
            TestBean object = new TestBean();
            object.setBirthDate(LocalDate.of(2000, 1, 2));
            object.setBirthDateWithCustomFormat(LocalDate.of(2001, 2, 3));
    
            // Assert serialization
            assertEquals(json, objectMapper.writeValueAsString(object));
    
            // Assert deserialization
            assertEquals(object, objectMapper.readValue(json, TestBean.class));
        }
    }
    

    TestBean uses Lombok to generate the boilerplate for the bean.

    0 讨论(0)
  • 2020-11-22 13:29

    Simplest and shortest so far:

    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate localDate;
    
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime localDateTime;
    

    no dependency required with Spring boot >= 2.2+

    0 讨论(0)
  • 2020-11-22 13:31

    The simplest solution (which supports deserialization and serialization as well) is

    import com.fasterxml.jackson.annotation.JsonFormat;
    import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
    import com.fasterxml.jackson.databind.annotation.JsonSerialize;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
    
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    private LocalDate dateOfBirth;
    

    While using the following dependencies in your project.

    Maven

    <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-databind</artifactId>
       <version>2.9.7</version>
    </dependency>
    <dependency>
       <groupId>com.fasterxml.jackson.datatype</groupId>
       <artifactId>jackson-datatype-jsr310</artifactId>
       <version>2.9.7</version>
    </dependency>
    

    Gradle

    compile "com.fasterxml.jackson.core:jackson-databind:2.9.7"
    compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.7"
    

    No additional implementation of a ContextResolver, Serializer or Deserializer is required.

    0 讨论(0)
  • 2020-11-22 13:32
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    

    works fine for me.

    0 讨论(0)
  • 2020-11-22 13:34

    Just an update of Christopher answer.

    Since the version 2.6.0

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

    Use the JavaTimeModule instead of JSR310Module (deprecated).

    @Provider
    public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {  
        private final ObjectMapper MAPPER;
    
        public ObjectMapperContextResolver() {
            MAPPER = new ObjectMapper();
            MAPPER.registerModule(new JavaTimeModule());
            MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        }
    
        @Override
        public ObjectMapper getContext(Class<?> type) {
            return MAPPER;
        }  
    }
    

    According to the documentation, the new JavaTimeModule uses same standard settings to default to serialization that does NOT use Timezone Ids, and instead only uses ISO-8601 compliant Timezone offsets.

    Behavior may be changed using SerializationFeature.WRITE_DATES_WITH_ZONE_ID

    0 讨论(0)
提交回复
热议问题