json 我可以从rest响应中接收一个Date属性,作为应用程序中的LocalDateTime或LocalDate吗?

bmp9r5qi  于 2022-11-19  发布在  其他
关注(0)|答案(1)|浏览(442)

我有这样的课:

public class MyLocalApplicationClass {
    private String name
    private LocalDateTime creationDate;
    private String createdBy;
}

在我从一个rest服务收到的响应中,我得到了这个对象(作为一个json):

public class MyRemoteApplicationClass {
    private String name
    private Date creationDate;
    private String createdBy;
}

因此,当我发送请求时,我会从MyRemoteApplicationClass中获取creationDate的值(json),如下所示:

{
    “name”:”anything”,
    "creation_date": 1666190973000,
    "created_by": “anyone”
}

因此,我想知道是否可以将其作为LocalDateTime接收,或者我应该将其作为Date接收,然后将其转换为LocalDateTime(我认为这是最好的选择)?
因为我尝试以LocalDateTime的形式接收它,但它抛出以下错误:

raw timestamp (1656015404000) not allowed for 
java.time.LocalDateTime: need additional information such as an
offset or time-zone (see class Javadocs)

此外,我尝试仅以LocalDate的形式接收它,但它抛出了以下错误(我已经添加了jsr310的依赖项,还添加了带有此注解@JsonSerialize(using = LocalDateSerializer.class) @JsonDeserialize(using = LocalDateDeserializer.class)的序列化器和反序列化器,因此我认为真实的的问题是警告)和警告:

Java 8 date/time type `java.time.DateTimeException` not supported
by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
to enable handling (through reference chain:
com.fasterxml.jackson.databind.JsonMappingException["cause"])

WARN -- com.fasterxml.jackson.databind.JsonMappingException:
Invalid value for EpochDay (valid values -365243219162 - 365241780471): 1656015404000
qpgpyjmq

qpgpyjmq1#

TL;DR

有几种可能的解决方案:

  • 按原样接收时间戳,并在构造函数中执行解析;
  • 通过注册JavaTimeModule并应用所需的时间戳精度来应用全局配置。
  • 实作自订还原序列化程式。

让我们来了解一下这些选项。

剖析建构函式内的时间戳记

我们可以在MyLocalApplicationClass内声明 *all-args构造函数 *,并使用@JsonProperty注解每个参数。*contranctior参数 * creationDate应作为long接收,因为它将被手动从epoch毫秒解析为LocalDateTime
它可能看起来像这样:

public class MyLocalApplicationClass {
    private String name;
    private LocalDateTime creationDate;
    private String createdBy;
    
    public MyLocalApplicationClass(@JsonProperty("name") String name,
                                   @JsonProperty("creation_date") long creationDate,
                                   @JsonProperty("created_by") String createdBy) {
        this.name = name;
        this.createdBy = createdBy;
        this.creationDate = Instant
            .ofEpochMilli(creationDate)
            .atZone(ZoneOffset.UTC)
            .toLocalDateTime();
    }
}

应用全局配置

另一个选项是应用全局反序列化配置。
但这需要将creationDate属性的类型更改为Instant
要正确配置ObjectMapper,我们需要采取两个步骤:

  • 寄存器JavaTimeModule模块;
  • 通过将解串行化属性DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS设置为false来向ObjectMapper指示时间戳的精度。

为此,我们可以将Jackson2ObjectMapperBuilderObjectMapper作为Bean放入Spring的上下文中。

@Configuration
public class JsonConfig {
    
    @Bean
    public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
        return new Jackson2ObjectMapperBuilder();
    }

    @Bean
    public ObjectMapper objectMapper() {
        return jackson2ObjectMapperBuilder()
            .build()
            .registerModule(new JavaTimeModule())
            .configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
    }
}

请注意creationDate的类型应为Instant,在这种情况下,仅因为属性和字段名称不匹配(* 它们与转换无关 *),才需要数据绑定注解

public class MyLocalApplicationClass {
    private String name;
    @JsonProperty("creation_date")
    private Instant creationDate;
    @JsonProperty("created_by")
    private String createdBy;
    
    // getter, setters, etc.
}

简化的全局配置

正如@M. Deinum在注解中所指出的,所需的精度可以通过application.properties进行配置:

spring.jackson.deserialization.READ_DATE_TIMESTAMPS_AS_NANOSECONDS=false

我们可以删除在上面示例的配置类中声明的两个Bean(* Map器和Map器构建器 *),因为JacksonAutoConfiguration会在应用程序启动时获取并注册所有已知模块。
MyLocalApplicationClass的声明与上面显示的声明相同(creationDate的类型为java.time.Instant)。

创建自定义反序列化程序

最后,我们可以通过扩展StdDeserializer并重写其abstract方法deserialize()来实现自定义的反序列化器。

public class DateTimeDeserializer extends StdDeserializer<LocalDateTime> {
    
    public DateTimeDeserializer() {
        super(LocalDateTime.class);
    }
    
    @Override
    public LocalDateTime deserialize(JsonParser p,
                                     DeserializationContext ctxt) throws IOException, JacksonException {
    
    
        JsonNode node = p.getCodec().readTree(p);
        long timestamp = node.longValue();
        
        return Instant
            .ofEpochMilli(timestamp)
            .atZone(ZoneOffset.UTC)
            .toLocalDateTime();
    }
}

要指示Jackson应用此反序列化程序,可以通过@JsonDeserialize注解的using属性来指定,该注解应放置在creationDate字段的顶部。

public class MyLocalApplicationClass {

    private String name;

    @JsonDeserialize(using = DateTimeDeserializer.class)
    @JsonProperty("creation_date")
    private LocalDateTime creationDate;

    @JsonProperty("created_by")
    private String createdBy;
    
    // getter, setters, etc.
}

相关问题