【问题标题】:Java 8 LocalDate Jackson formatJava 8 LocalDate 杰克逊格式
【发布时间】:2015-05-02 09:03:02
【问题描述】:

对于 java.util.Date 当我这样做时

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

然后在我发送的 JSON 请求中

{ {"dateOfBirth":"01/01/2000"} }  

它有效。

我应该如何为 Java 8 的 LocalDate 字段执行此操作??

我试过了

@JsonDeserialize(using = LocalDateDeserializer.class)  
@JsonSerialize(using = LocalDateSerializer.class)  
private LocalDate dateOfBirth;  

没用。

有人可以告诉我这样做的正确方法是什么..

以下是依赖项

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>jaxrs-api</artifactId>
     <version>3.0.9.Final</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
    <version>2.4.2</version>
</dependency>
<dependency>
    <groupId>com.wordnik</groupId>
    <artifactId>swagger-annotations</artifactId>
    <version>1.3.10</version>
</dependency>

【问题讨论】:

    标签: java json jackson jax-rs resteasy


    【解决方案1】:

    我从来没有能够使用注释让这个工作变得简单。为了让它工作,我为ObjectMapper 创建了一个ContextResolver,然后我添加了JSR310Module更新:现在改为JavaTimeModule),还有一个警告,即是否需要将 write-date-as-timestamp 设置为 false。在the documentation for the JSR310 module 上查看更多信息。这是我使用的示例。

    依赖

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

    注意: 我遇到的一个问题是 jackson-annotation 版本被另一个依赖项引入,使用版本 2.3.2,它取消了 @987654331 所需的 2.4 @。发生的事情是我得到了 ObjectIdResolver 的 NoClassDefFound,这是一个 2.4 类。所以我只需要排列包含的依赖版本

    上下文解析器

    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;
        }  
    }
    

    资源类

    @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;
        }
    }
    

    测试

    curl -v http://localhost:8080/api/person
    结果: {"birthDate":"2015-03-01"}

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


    有关 JAXB 解决方案,另请参阅 here

    更新

    JSR310Module 自 Jackson 2.7 版起已弃用。相反,您应该注册模块JavaTimeModule。还是一样的依赖。

    【讨论】:

    • 嗨 Peeskillet ,字段birthDate 正在生成为 "birthDate": { "year": 0, "month": "Month", "dayOfMonth": 0, "dayOfWeek": "DayOfWeek ", "era": { "value": 0 }, "dayOfYear": 0, "leapYear": false, "monthValue": 0, "chronology": { "id": "", "calendarType": "" } } 我怎样才能让它像“birthDate”一样???
    • 检查 ContextResolver 是否被调用。在getContext 方法中添加打印语句。如果调用此方法,我看不出它不起作用的原因。如果它没有被调用,那么它可能需要通过应用程序配置来修复。为此,我需要看到的不仅仅是您提供的内容。像 Resteasy 版本、依赖项、应用程序配置 web.xml 或 Application 子类。基本上足以重现问题
    • ContextResolver 没有被称为 Peeskillet 。我在 web.xml 中将它重新注册为 resteasy.resourcescom.bac.ObjectMapperContextResolver我正在使用的依赖项的更新问题
    • Swagger 似乎是问题所在。我会说禁用它,但看到from this question 有一个问题已提交,Swagger 的 ObjectMapper 与尝试使用您自己的之间存在冲突。您可以尝试禁用他们的,然后在 ContextResolver 中,将所有配置设置为 ObjectMapper,就像 swagger 所做的那样(您可以在问题中看到一个链接)。我不知道,因为我不怎么招摇。但我认为招摇是主要问题,为什么没有调用 contextresolver。
    • com.fasterxml.jackson.datatype.jsr310.JSR310Module 在 2.5 版中已弃用,建议使用较新的 com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
    【解决方案2】:
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    

    【讨论】:

    • new com.fasterxml.jackson.datatype.jsr310.JSR310Module() 适用于 Jackson 的 2.5.4 版本。此版本中不存在 JavaTimeModule 类。
    • 这个答案也适用于LocalDateTime (jackson 2.9.5)。需要 1 个额外的依赖项,所以我的 build.sbt 看起来像:"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.9.5", "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.9.5"
    • 这为我指明了正确的方向,谢谢!我想在spring-boot中添加你需要做的就是将以下内容添加到application.properties:spring.jackson.serialization.write-dates-as-timestamps=false
    • 是的,ObjectMapper bean 做到了。谢谢!
    【解决方案3】:

    @JsonSerialize 和 @JsonDeserialize 对我来说效果很好。它们消除了导入额外 jsr310 模块的需要:

    @JsonDeserialize(using = LocalDateDeserializer.class)  
    @JsonSerialize(using = LocalDateSerializer.class)  
    private LocalDate dateOfBirth;
    

    反序列化器:

    public class LocalDateDeserializer extends StdDeserializer<LocalDate> {
    
        private static final long serialVersionUID = 1L;
    
        protected LocalDateDeserializer() {
            super(LocalDate.class);
        }
    
    
        @Override
        public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            return LocalDate.parse(jp.readValueAs(String.class));
        }
    
    }
    

    序列化器:

    public class LocalDateSerializer extends StdSerializer<LocalDate> {
    
        private static final long serialVersionUID = 1L;
    
        public LocalDateSerializer(){
            super(LocalDate.class);
        }
    
        @Override
        public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider sp) throws IOException, JsonProcessingException {
            gen.writeString(value.format(DateTimeFormatter.ISO_LOCAL_DATE));
        }
    }
    

    【讨论】:

    • 这些类包含在jackson-datatype-jsr310 中。无需在您的项目中手动定义它们。
    • 这个解决方案对我有用,使用jackson-datatype-jsr310 中的序列化程序。
    • 如果您在 jackson-datatype-jsr310 中使用序列化器和反序列化器,最好将 @JsonFormat(shape = JsonFormat.Shape.STRING) 添加到您的字段中。如果没有这种格式,该值将被序列化为 [年、月、日],尽管反序列化会起作用。
    • 这个答案对我不起作用 Jackson version 2.9.0
    【解决方案4】:

    在 Spring Boot Web 应用中,使用 JacksonJSR 310 版本“2.8.5”

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

    @JsonFormat 有效:

    import com.fasterxml.jackson.annotation.JsonFormat;
    
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private LocalDate birthDate;
    

    【讨论】:

    • 这对反序列化有用吗?还是只有序列化?反序列化没有成功
    • 我必须明确声明解串器@JsonDeserialize(using= LocalDateDeserializer.class)
    • @JsonFormat 仅用于更改输出数据格式。 stackoverflow.com/a/53251526/816759@JsonFormat@JsonDeserialize@JsonSerialize 完美搭配
    • 在 Spring Boot 中,添加 JSR310 依赖项后,您只需将 spring.jackson.serialization.write-dates-as-timestamps=false 添加到您的 application.properties,它会自动将其格式化为 yyyy-MM-dd。不需要@JsonFormat
    • 最简单的解决方案。
    【解决方案5】:

    只是克里斯托弗答案的更新。

    2.6.0版本起

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

    使用 JavaTimeModule 代替 JSR310Module(已弃用)。

    @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;
        }  
    }
    

    根据documentation,新的 JavaTimeModule 使用相同的标准设置默认为不使用时区 ID 的序列化,而是仅使用符合 ISO-8601 的时区偏移量。

    可以使用 SerializationFeature.WRITE_DATES_WITH_ZONE_ID 改变行为

    【讨论】:

    • 这对我有帮助。就我而言,我需要添加 MAPPER.registerModule(new JavaTimeModule()); 行。它让我将 LocalDate 对象格式化为“2020-02-20”格式。我不需要 MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); 行,因为我正在寻找什么
    【解决方案6】:

    由于LocalDateSerializer 默认将其转换为“[year,month,day]”(一个 json 数组)而不是“year-month-day”(一个 json 字符串),并且因为我不想要求任何特殊的ObjectMapper 设置(如果禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,则可以使LocalDateSerializer 生成字符串,但这需要对ObjectMapper 进行额外设置),我使用以下内容:

    进口:

    import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
    

    代码:

    // generates "yyyy-MM-dd" output
    @JsonSerialize(using = ToStringSerializer.class)
    // handles "yyyy-MM-dd" input just fine (note: "yyyy-M-d" format will not work)
    @JsonDeserialize(using = LocalDateDeserializer.class)
    private LocalDate localDate;
    

    现在我可以使用new ObjectMapper() 来读取和写入我的对象,而无需任何特殊设置。

    【讨论】:

    • 我想添加的一件事是将日期传递为"2018-12-07" 而不是"2018-12-7" 否则你会得到一个错误。
    • 正确,它适用于yyyy-MM-dd(2 位数的月份和日期)格式,而不是yyyy-M-d(1 位数的月份或日期)格式。
    【解决方案7】:

    最简单的解决方案(也支持反序列化和序列化)是

    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;
    

    在您的项目中使用以下依赖项时。

    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"
    

    不需要额外实现 ContextResolver、Serializer 或 Deserializer。

    【讨论】:

    • 太棒了,绝对是最简单的。仅供参考,对于任何有很多依赖项的人,我不得不更新一些包含杰克逊注释的其他库。
    • 这个答案是我解决问题的最接近的答案。序列化工作正常,但反序列化失败,因为我认为我与@JsonFormat 一起使用的模式(@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy_HH:mm:SS")。
    • 如果您的反序列化失败,很可能是您的ObjectMapper 没有注册JavaTimeModule。如果您的 ObjectMapper 实例是从 spring/MessageConverter 框架提供的。他们做了一些魔法把它们连接起来。在其他情况下,应该registerModule 默认为 POJO 中的所有“LocalDate”启用LocalDateDeserializer
    • 看了这么多解决方案后,这对我有用。对我来说,日期是“yyyyMMdd”格式,它就像魅力一样。谢谢
    • @vikastiwari,很高兴听到这个消息。这很容易:)
    【解决方案8】:

    https://stackoverflow.com/a/53251526/1282532 是序列化/反序列化属性的最简单方法。我对这种方法有两个担忧——在某种程度上违反了 DRY 原则以及 pojo 和 mapper 之间的高度耦合。

    public class Trade {
        @JsonFormat(pattern = "yyyyMMdd")
        @JsonDeserialize(using = LocalDateDeserializer.class)
        @JsonSerialize(using = LocalDateSerializer.class)
        private LocalDate tradeDate;
        @JsonFormat(pattern = "yyyyMMdd")
        @JsonDeserialize(using = LocalDateDeserializer.class)
        @JsonSerialize(using = LocalDateSerializer.class)
        private LocalDate maturityDate;
        @JsonFormat(pattern = "yyyyMMdd")
        @JsonDeserialize(using = LocalDateDeserializer.class)
        @JsonSerialize(using = LocalDateSerializer.class)
        private LocalDate entryDate;
    }
    

    如果您的 POJO 具有多个 LocalDate 字段,最好配置映射器而不是 POJO。如果您使用的是 ISO-8601 值(“2019-01-31”),则可以像 https://stackoverflow.com/a/35062824/1282532 一样简单

    如果您需要处理自定义格式,代码将如下所示:

    ObjectMapper mapper = new ObjectMapper();
    JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyyMMdd")));
    javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyyMMdd")));
    mapper.registerModule(javaTimeModule);
    

    逻辑只写一次,可重复用于多个POJO

    【讨论】:

      【解决方案9】:

      在配置类中定义 LocalDateSerializerLocalDateDeserializer 类并通过 JavaTimeModule 将它们注册到 ObjectMapper,如下所示:

      @Configuration
      public class AppConfig
      {
      @Bean
          public ObjectMapper objectMapper()
          {
              ObjectMapper mapper = new ObjectMapper();
              mapper.setSerializationInclusion(Include.NON_EMPTY);
              //other mapper configs
              // Customize de-serialization
      
      
              JavaTimeModule javaTimeModule = new JavaTimeModule();
              javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
              javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
              mapper.registerModule(javaTimeModule);
      
              return mapper;
          }
      
          public class LocalDateSerializer extends JsonSerializer<LocalDate> {
              @Override
              public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                  gen.writeString(value.format(Constant.DATE_TIME_FORMATTER));
              }
          }
      
          public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
      
              @Override
              public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
                  return LocalDate.parse(p.getValueAsString(), Constant.DATE_TIME_FORMATTER);
              }
          }
      }
      

      【讨论】:

        【解决方案10】:

        以下注释对我来说很好用。

        不需要额外的依赖。

            @JsonProperty("created_at")
            @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
            @JsonDeserialize(using = LocalDateTimeDeserializer.class)
            @JsonSerialize(using = LocalDateTimeSerializer.class)
            private LocalDateTime createdAt;
        

        【讨论】:

          【解决方案11】:

          从 2020 年和 Jackson 2.10.1 开始,不需要任何特殊代码,只需告诉 Jackson 你想要什么:

          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);
          

          这在answer 中已经提到,我正在添加一个单元测试来验证功能:

          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 使用 Lombok 为 bean 生成样板。

          【讨论】:

            【解决方案12】:

            迄今为止最简单最短的:

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

            Spring boot >= 2.2+ 不需要依赖项

            【讨论】:

              【解决方案13】:

              如果您的请求包含这样的对象:

              {
                  "year": 1900,
                  "month": 1,
                  "day": 20
              }
              

              那么你可以使用:

              data class DateObject(
                  val day: Int,
                  val month: Int,
                  val year: Int
              )
              class LocalDateConverter : StdConverter<DateObject, LocalDate>() {
                  override fun convert(value: DateObject): LocalDate {
                      return value.run { LocalDate.of(year, month, day) }
                  }
              }
              

              场地上方:

              @JsonDeserialize(converter = LocalDateConverter::class)
              val dateOfBirth: LocalDate
              

              代码在 Kotlin 中,但这当然也适用于 Java。

              【讨论】:

                【解决方案14】:
                @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
                @JsonSerialize(using = LocalDateTimeSerializer.class)
                @JsonDeserialize(using = LocalDateTimeDeserializer.class)
                private LocalDateTime createdDate;
                

                【讨论】:

                • 这行得通。还需要jackson-datatype-jsr310的依赖
                • 你拯救了我的周末!
                • 我刚刚遇到了同样的问题,这个解决方案效果很好。谢谢@slisnychyi
                【解决方案15】:

                Pojo 中的注释而不使用额外的依赖项

                @DateTimeFormat (pattern = "yyyy/MM/dd", iso = DateTimeFormat.ISO.DATE)
                private LocalDate enddate;
                

                【讨论】:

                • 你确定“没有额外的依赖”吗?我有 Jackson 2.12.4,找不到这个注释。
                【解决方案16】:

                使用 spring boot 2.3.9.RELEASE,我刚刚在具有 LocalDate 字段的 POJO 类中注册了没有显式注释的 java 时间模块,并且它起作用了。

                var objectMapper = new ObjectMapper();
                objectMapper.registerModule(new JavaTimeModule());
                

                【讨论】:

                  【解决方案17】:

                  对于 Spring 来说更容易一些:

                  ///...
                  
                  @Configuration
                  public class ApplicationCtxBeans {
                  //....
                      @Bean
                      public ObjectMapper objectMapper() {
                          ObjectMapper MAPPER = new ObjectMapper(); 
                          MAPPER.registerModule(new JavaTimeModule()); // to handle LocalDateTime etc
                          return MAPPER;
                      }
                  //...
                  }
                  

                  用法:

                  @Service
                  public class SomeService {
                      
                  //...
                      @Autowired
                      ObjectMapper jsonMapper;
                  //...
                    JsonNode node = jsonMapper.readTree(
                      jsonMapper.writeValueAsString(instance_Of_Class_With_LocalDate_Fields)
                    );
                  //...
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2017-07-01
                    • 2016-07-21
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-07-31
                    • 1970-01-01
                    相关资源
                    最近更新 更多