当前位置: 首页 > 面试题库 >

杰克逊:解析自定义偏移日期时间

汪辰阳
2023-03-14
问题内容

我有一个带有时间戳属性的模型:

class Model {
    @JsonProperty("timestamp")
    private OffsetDateTime timestamp;
}

时间戳的格式如下:

2017-09-17 13:45:42.710576+02

OffsetDateTime 无法解析此:

com.fasterxml.jackson.databind.exc.InvalidFormatException:无法java.time.OffsetDateTime从字符串“
2017-09-17 13:45:42.710576 + 02” 反序列化类型的值:文本“ 2017-09-17 13:45:42.710576 +
02”可能不能在索引10处解析

我怎样才能解决这个问题?


问题答案:

您必须告诉杰克逊日期是什么格式。基本上,您year-month- day紧接着是hour:minute:second.microseconds和带有2个数字的偏移量(+02)。因此,您的模式将是:

@JsonProperty("timestamp")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSSSSSx")
private OffsetDateTime timestamp;

查看所有日期/时间模式以获得更详细的说明。

如果要在中保留相同的offset(+02OffsetDateTime,请不要忘记将DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE选项调整为false

如果将此选项设置为true(在我的测试中),结果将转换为UTC(但实际上将转换为Jackson中配置的任何时区):

2017-09-17T11:45:42.710576Z

如果将设置为false,则会保留输入中使用的偏移量:

2017-09-17T13:45:42.710576 + 02:00

上面的代码仅在小数点后6位有效。但是,如果此数量有所变化,则可以使用以分隔的可选模式[]

示例:如果输入可以有6或3个十进制数字,则可以使用pattern = "yyyy-MM-dd HH:mm:ss.[SSSSSS][SSS]x"。可选部分,[SSSSSS][SSS]告诉解析器考虑6位或3位数字。

可选模式的问题在于,在序列化时,它会打印所有模式(因此它将打印秒的小数两次:6位 3位)。

另一种选择是创建自定义的序列化器和反序列化器(通过扩展com.fasterxml.jackson.databind.JsonSerializercom.fasterxml.jackson.databind.JsonDeserializer):

public class CustomDeserializer extends JsonDeserializer<OffsetDateTime> {

    private DateTimeFormatter formatter;

    public CustomDeserializer(DateTimeFormatter formatter) {
        this.formatter = formatter;
    }

    @Override
    public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
        return OffsetDateTime.parse(parser.getText(), this.formatter);
    }
}

public class CustomSerializer extends JsonSerializer<OffsetDateTime> {

    private DateTimeFormatter formatter;

    public CustomSerializer(DateTimeFormatter formatter) {
        this.formatter = formatter;
    }

    @Override
    public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
        gen.writeString(value.format(this.formatter));
    }
}

然后,您可以在中注册JavaTimeModule。如何配置它取决于您所使用的环境(例如:在Spring中,您可以在xml文件中进行配置)。我将以编程方式作为示例。

首先,我使用来创建格式化程序java.time.format.DateTimeFormatterBuilder

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // date/time
    .appendPattern("yyyy-MM-dd HH:mm:ss")
    // optional fraction of seconds (from 0 to 9 digits)
    .optionalStart().appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).optionalEnd()
    // offset
    .appendPattern("x")
    // create formatter
    .toFormatter();

此格式化程序接受0到9位数字的可选秒数。然后,我使用上面的自定义类并在中注册它们ObjectMapper

// set formatter in the module and register in object mapper
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(OffsetDateTime.class, new CustomSerializer(formatter));
module.addDeserializer(OffsetDateTime.class, new CustomDeserializer(formatter));
mapper.registerModule(module);

我还@JsonFormat从字段中删除了注释:

@JsonProperty("timestamp")
private OffsetDateTime timestamp;

现在,它接受的值2017-09-17 13:45:42+02(不包括秒)和2017-09-17 13:45:42.71014+02(5个十进制数字)。它可以解析0到9个十进制数字(9是API支持的最大值),并且在序列化时打印完全相同的数量。

上面的替代方法非常灵活,因为它允许在自定义类中设置格式化程序。但是,它还会为所有OffsetDateTime字段设置序列化和反序列化。

如果您不想这样做,还可以使用固定的格式化程序创建一个类:

static class CustomDeserializer extends JsonDeserializer<OffsetDateTime> {

    private DateTimeFormatter formatter = // create formatter as above

    // deserialize method is the same
}

static class CustomSerializer extends JsonSerializer<OffsetDateTime> {

    private DateTimeFormatter formatter = // create formatter as above

    // serialize method is the same
}

然后,您可以使用注释com.fasterxml.jackson.databind.annotation.JsonSerialize和将这些仅添加到所需的字段中com.fasterxml.jackson.databind.annotation.JsonDeserialize

@JsonProperty("timestamp")
@JsonSerialize(using = CustomSerializer.class)
@JsonDeserialize(using = CustomDeserializer.class)
private OffsetDateTime timestamp;

这样,您无需在模块中注册自定义序列化程序,只有注释字段将使用自定义类(其他OffsetDateTime字段将使用默认设置)。



 类似资料:
  • 问题内容: 我需要设置班级日期序列化的格式。我有Jackson的版本,没有@JsonFormat。这就是为什么我编写自定义类的原因: } 并使用它: 但是,我还有另一个具有不同日期格式的字段,并且我不想创建用于序列化的其他类。我可以将所有需要的格式(例如常量)添加到CDJsonDateSerializer类中,并通过注释设置所需的格式吗?像这样: 。 下面的答案后: 经过一些更正后,它可以工作。我

  • 我试图在Jackson的spring boot应用程序中从JSON获取日期字段。JSONFormat如下所示: 它在大多数情况下都能正常工作,但当我通过2017-0526时,它会自动将其转换为2018年5月10日。 如果日期不是yyyyMMdd格式或包含减号,我想引发异常。我试图查看堆栈溢出和Jackson文档,但找不到任何东西。 为什么JsonFormat接受负日期? 对此是否有任何解决方法,以

  • 我对jackson进行了配置,以便在和时给出一个简单的字符串表示。这可以在序列化过程中找到,例如,当我在REST API上获得数据时。 但反过来就不行了。当我试图将数据发送到服务器,并且应该将JSON解析为java对象时,会引发此异常: 很抱歉没有提到我在单元测试中。 TestClass:

  • 提前感谢!

  • 问题内容: 我需要将以下格式的String解析为UTC中的LocalDate(或其他)。问题是以下代码: 输出忽略偏移量。所需的输出是 提前致谢! 问题答案: 最简单的答案是用来表示数据,但是您需要默认时间: 在处理时区时很有用,但是在仅处理偏移量时,它会更简单。 通常,应用程序代码不应包含type的变量。如果您看到了,通常会有更好的方法。

  • 我想使用Spring的RestTemplate plus Jackson来使用Web服务。我已经学习了几本教程,并且已经达到了创建DAO的目的。这是我获取所有域对象的方法: 但我的Web服务不会立即返回Station对象数组,而是以这种方式返回一个更具语义的表达式: 所以我的问题是,我不知道如何“告诉”RestTemplate在“stations”指示符之后立即解析对象列表,而不创建临时对象,这似