How to parse different ISO date/time formats with Jackson and java.time?
我们的Rest API从多个外部参与者获取JSON输入。 它们都使用" ISO-ish"格式,但是时区偏移量的格式略有不同。 这些是我们看到的一些最常见的格式:
1 2 3 4 5 6 | 2018-01-01T15:56:31.410Z 2018-01-01T15:56:31.41Z 2018-01-01T15:56:31Z 2018-01-01T15:56:31+00:00 2018-01-01T15:56:31+0000 2018-01-01T15:56:31+00 |
我们的堆栈是带有Jackson ObjectMapper的Spring Boot 2.0。 在我们的数据类中,我们经常使用类型
一些开发人员已尝试实现一种解析上述所有格式的解决方案,但均未成功。 特别是带有冒号(
如果解决方案能在模型的每个日期/时间字段上都不必添加注释,那么解决方案就可以了。
尊敬的社区,您有解决方案吗?
一种选择是创建自定义解串器。首先,您要注释各自的字段:
1 2 | @JsonDeserialize(using = OffsetDateTimeDeserializer.class) private OffsetDateTime date; |
然后创建反序列化器。它使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class OffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> { private DateTimeFormatter fmt = new DateTimeFormatterBuilder() // date/time .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) // offset (hh:mm -"+00:00" when it's zero) .optionalStart().appendOffset("+HH:MM","+00:00").optionalEnd() // offset (hhmm -"+0000" when it's zero) .optionalStart().appendOffset("+HHMM","+0000").optionalEnd() // offset (hh -"+00" when it's zero) .optionalStart().appendOffset("+HH","+00").optionalEnd() // offset (pattern"X" uses"Z" for zero offset) .optionalStart().appendPattern("X").optionalEnd() // create formatter .toFormatter(); @Override public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { return OffsetDateTime.parse(p.getText(), fmt); } } |
我还使用了内置常量
我正在使用JDK 1.8.0_144,发现了一个较短(但不多)的解决方案:
1 2 3 4 5 6 7 8 9 | private DateTimeFormatter fmt = new DateTimeFormatterBuilder() // date/time .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) // offset +00:00 or Z .optionalStart().appendOffset("+HH:MM","Z").optionalEnd() // offset +0000, +00 or Z .optionalStart().appendOffset("+HHmm","Z").optionalEnd() // create formatter .toFormatter(); |
您可以做的另一项改进是将格式化程序更改为
非常感谢您的所有投入!
我选择了jeedas建议的解串器和Ole V.V建议的格式化器(因为它更短)。
1 2 3 4 5 6 7 8 9 10 11 | class DefensiveIsoOffsetDateTimeDeserializer : JsonDeserializer<OffsetDateTime>() { private val formatter = DateTimeFormatterBuilder() .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) .appendPattern("[XXX][XX][X]") .toFormatter() override fun deserialize(p: JsonParser, ctxt: DeserializationContext) = OffsetDateTime.parse(p.text, formatter) override fun handledType() = OffsetDateTime::class.java } |
我还添加了一个自定义序列化程序,以确保在生成json时我们使用正确的格式:
1 2 3 4 5 6 7 8 9 | class OffsetDateTimeSerializer: JsonSerializer<OffsetDateTime>() { override fun serialize( value: OffsetDateTime, gen: JsonGenerator, serializers: SerializerProvider ) = gen.writeString(value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)) override fun handledType() = OffsetDateTime::class.java } |
将所有部分放在一起,我在spring类路径中添加了一个
1 2 3 4 5 6 7 8 9 | @Configuration open class JacksonConfig { @Bean open fun jacksonCustomizer() = Jackson2ObjectMapperBuilderCustomizer { it.deserializers(DefensiveIsoOffsetDateTimeDeserializer()) it.serializers(OffsetDateTimeSerializer()) } } |
这仅是答案的四分之一。我既没有Kotlin的经验,也没有Jackson的经验,但是我想为Java提供一些解决方案。如果您能以某种方式使它们适合整体解决方案,我将感到高兴。
1 2 | String modifiedEx = ex.replaceFirst("(\\\\d{2})(\\\\d{2})$","$1:$2"); System.out.println(OffsetDateTime.parse(modifiedEx)); |
在我的Java 9(9.0.4)上,一个参数
在Java 8中也可以使用的更好的解决方案(我已经测试过):
1 2 3 4 5 | DateTimeFormatter formatter = new DateTimeFormatterBuilder() .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) .appendPattern("[XXX][XX][X]") .toFormatter(); System.out.println(OffsetDateTime.parse(ex, formatter)); |
模式