为了账号安全,请及时绑定邮箱和手机立即绑定

使用 objectMapper 将 JSON 日期格式反序列化为 ZonedDateTime

使用 objectMapper 将 JSON 日期格式反序列化为 ZonedDateTime

红颜莎娜 2023-04-13 17:19:58
背景我有以下 JSON(来自 Kafka 的消息){      "markdownPercentage": 20,      "currency": "SEK",      "startDate": "2019-07-25"}我有以下(生成的 JSON 模式)POJO(我无法更改 POJO,因为它是公司的共享资源)public class Markdown {    @JsonProperty("markdownPercentage")    @NotNull    private Integer markdownPercentage = 0;    @JsonProperty("currency")    @NotNull    private String currency = "";    @JsonFormat(        shape = Shape.STRING,        pattern = "yyyy-MM-dd"    )    @JsonProperty("startDate")    @NotNull    private ZonedDateTime startDate;    // Constructors, Getters, Setters etc.}我们的应用程序是一个 Spring Boot 应用程序,它使用 Spring Cloud Stream 从 Kafka 读取 JSON 消息 (1) 并使用 POJO (2) 然后对其进行处理。问题当应用程序尝试将消息反序列化为对象时,它会抛出异常当前代码我定义了以下 objectMapper/**     * Date mapper.     *     * @return the {@link ObjectMapper}     */    @Bean    public ObjectMapper objectMapper() {        ObjectMapper mapper = new ObjectMapper();        mapper.registerModule(new JavaTimeModule());        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));        return mapper;    }问题我知道 POJO 中生成的 ZonedDateTime 需要源消息中不存在的“时间”元素。我只能控制 objectMapper。是否有任何可能的配置可以使这项工作?笔记如果反序列化 POJO 中的时间元素“假定”为 startOfDay,即“00.00.00.000Z”,我很好
查看完整描述

5 回答

?
交互式爱情

TA贡献1712条经验 获得超3个赞

我只能控制ObjectMapper. 是否有任何可能的配置可以使这项工作?


只要您对时间和时区的默认值感到满意,就可以使用自定义反序列化器解决它:


public class ZonedDateTimeDeserializer extends JsonDeserializer<ZonedDateTime> {


    @Override

    public ZonedDateTime deserialize(JsonParser jsonParser,

                                     DeserializationContext deserializationContext)

                                     throws IOException {


        LocalDate localDate = LocalDate.parse(

                jsonParser.getText(), 

                DateTimeFormatter.ISO_LOCAL_DATE);


        return localDate.atStartOfDay(ZoneOffset.UTC);

    }

}

然后将其添加到模块并将模块注册到您的ObjectMapper实例:


SimpleModule module = new SimpleModule();

module.addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer());


ObjectMapper mapper = new ObjectMapper();

mapper.registerModule(module);

如果将反序列化器添加到模块中不适合您(从某种意义上说,此配置将应用于其他ZonedDateTime实例),那么您可以依靠混合来定义反序列化器将应用于哪些字段。首先定义一个mix-in接口,如下图:


public interface MarkdownMixIn {


    @JsonDeserialize(using = ZonedDateTimeDeserializer.class)

    ZonedDateTime getDate();

}

然后将混合接口绑定到所需的类:


ObjectMapper mapper = new ObjectMapper();

mapper.addMixIn(Markdown.class, MarkdownMixIn.class);


查看完整回答
反对 回复 2023-04-13
?
长风秋雁

TA贡献1757条经验 获得超7个赞

问题:我想将日期从 json 解析为 java LocalDateTime/ZonedDateTime 对象。ZonedDateTimeSerializer 存在但 ZonedDateTimeDeserializer 不存在。因此,为什么我创建了一个自定义 ZonedDateTimeDeserializer。


  public static final String ZONED_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSz"; 


  @Getter

  @Setter

  @JsonSerialize(using = ZonedDateTimeSerializer.class)

  @JsonDeserialize(using = ZonedDateTimeDeserializer.class) // Doesn't exist, So I created a custom ZonedDateDeserializer utility class.

  @JsonFormat(pattern = ZONED_DATE_TIME_FORMAT)

  @JsonProperty("lastUpdated")

  private ZonedDateTime lastUpdated;

解决方案:我最终得到了更简单、更少的代码行。


用于反序列化ZonedDateTime的实用程序类:


/**

 * Custom {@link ZonedDateTime} deserializer.

 *

 * @param jsonParser             for extracting the date in {@link String} format.

 * @param deserializationContext for the process of deserialization a single root-level value.

 * @return {@link ZonedDateTime} object of the date.

 * @throws IOException throws I/O exceptions.

 */

public class ZonedDateTimeDeserializer extends JsonDeserializer<ZonedDateTime> {


    @Override

    public ZonedDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)

            throws IOException {


        return ZonedDateTime.parse(jsonParser.getText(), DateTimeFormatter.ofPattern(ZONED_DATE_TIME_FORMAT));

    }

}

如果您改用LocalDateTime怎么办。在那种情况下,它甚至更容易,反序列化器和序列化器类都已经提供给我们了。不需要上面定义的自定义实用程序类:


  @Getter

  @Setter

  @JsonSerialize(using = LocalDateSerializer.class)

  @JsonDeserialize(using = LocalDateTimeDeserializer.class)

  @JsonFormat(pattern = ZONED_DATE_TIME_FORMAT) //Specify the format you want: "yyyy-MM-dd'T'HH:mm:ss.SSS"

  @JsonProperty("created")

  private LocalDateTime created;

关键词:json格式 localDateTime zonedDateTime


查看完整回答
反对 回复 2023-04-13
?
Qyouu

TA贡献1786条经验 获得超11个赞

不幸的是,默认情况下您不能反序列化为String Object格式ZonedDateTime。但是您可以通过两种方式克服这个问题。


方式01


ZonedDateTime将类型更改为LocalDate在您的类中键入POJO并将值作为字符串传递03-06-2012


方式02


但是如果您必须使用时区存储日期和时间,那么您必须执行以下方法来克服


步骤01


ZonedDateTime使用 DateTimeFormat创建反序列化类


public class ZonedDateTimeDeserializer extends JsonDeserializer<ZonedDateTime> {


    @Override

    public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {


        DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss z");

        LocalDate localDate = LocalDate.parse(p.getText(),dateTimeFormatter);


        return localDate.atStartOfDay(ZoneOffset.UTC);

    }

}

步骤02


@JsonDeserialize在方法级别注释的支持下,您必须在 POJO 类中将反序列化类与受影响的字段一起使用。


@JsonDeserialize(using = ZonedDateTimeDeserializer.class)

private ZonedDateTime startDate;

步骤03


以 ZonedDateTimeDeserializer 类给出的上述格式将值作为字符串传递


       "startDate" : "09-03-2003 10:15:00 Europe/Paris"


查看完整回答
反对 回复 2023-04-13
?
梵蒂冈之花

TA贡献1900条经验 获得超5个赞

遗憾的是,如果不将 POJO 的类型更改为 LocalDate,那将很困难。

我能想到的最接近的解决方案是为杰克逊写一个习惯JsonDeserializer,这绝对不是那种事情的好习惯。


查看完整回答
反对 回复 2023-04-13
?
HUX布斯

TA贡献1876条经验 获得超6个赞

您可以编写自己的反序列化器,如@cassiomolin答案中所示。但也有另一种选择。在堆栈跟踪中,我们有DeserializationContext.weirdStringException方法允许我们提供DeserializationProblemHandler处理奇怪字符串值的方法。请参阅以下示例:


import com.fasterxml.jackson.annotation.JsonFormat;

import com.fasterxml.jackson.annotation.JsonFormat.Shape;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.DeserializationContext;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;

import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import java.io.IOException;

import java.time.LocalDate;

import java.time.ZonedDateTime;

import java.time.format.DateTimeFormatter;

import java.util.TimeZone;


public class AppJson {


    public static void main(String[] args) throws IOException {

        ObjectMapper mapper = new ObjectMapper();

        // override default time zone if needed

        mapper.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));


        mapper.registerModule(new JavaTimeModule());

        mapper.addHandler(new DeserializationProblemHandler() {

            @Override

            public Object handleWeirdStringValue(DeserializationContext ctxt, Class<?> targetType,

                String valueToConvert, String failureMsg) {

                LocalDate date = LocalDate.parse(valueToConvert, DateTimeFormatter.ISO_DATE);

                return date.atStartOfDay(ctxt.getTimeZone().toZoneId());

            }

        });

        String json = "{\"startDate\": \"2019-07-25\"}";

        Markdown markdown = mapper.readValue(json, Markdown.class);

        System.out.println(markdown);

    }

}

上面的代码打印:


Markdown{startDate=2019-07-25T00:00-07:00[America/Los_Angeles]}


查看完整回答
反对 回复 2023-04-13
  • 5 回答
  • 0 关注
  • 279 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信