我们的Rest API从多个外部方获取JSON输入。他们都使用" ISO-ish"格式,但时区偏移的格式略有不同。这些是我们看到的一些最常见的格式:
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。在我们的数据类中,我们经常使用类型java.time.OffsetDateTime
。
一些开发人员试图实现解析所有上述格式的解决方案,但都没有成功。特别是带冒号(00:00
)的第四个变体似乎是不可解析的。
如果解决方案无需在模型的每个日期/时间字段上放置注释,那就太棒了。
亲爱的社区,您有解决方案吗?
答案 0 :(得分:2)
另一种方法是创建自定义反序列化器。首先,您注释相应的字段:
df[['a','b','c']].plot(ax = ax2, color=colors)
然后创建反序列化器。它使用java.time.format.DateTimeFormatterBuilder
,使用许多可选部分来处理所有不同类型的偏移:
@JsonDeserialize(using = OffsetDateTimeDeserializer.class)
private OffsetDateTime date;
我还使用了内置常量DateTimeFormatter.ISO_LOCAL_DATE_TIME
,因为它处理了可选的秒数 - 并且小数位的数量似乎也是可变的,并且这个内置格式化器已经处理好了这些细节适合你。
我使用JDK 1.8.0_144并找到了一个更短(但不多)的解决方案:
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);
}
}
您可以做的另一项改进是将格式化程序更改为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();
,because this class is immutable and thread-safe。
答案 1 :(得分:1)
这只是答案的四分之一。我既没有Kotlin也没有Jackson的经验,但我有几个我想贡献的Java解决方案。如果你能以某种方式将它们整合到一个完整的解决方案中,我会很高兴。
String modifiedEx = ex.replaceFirst("(\\d{2})(\\d{2})$", "$1:$2");
System.out.println(OffsetDateTime.parse(modifiedEx));
在我的Java 9(9.0.4)上,one-arg OffsetDateTime.parse
解析所有示例字符串,但偏移+0000
没有冒号的字符串除外。所以我的黑客是插入该冒号然后解析。以上解析所有字符串。它在Java 8中不起作用(从Java 8到Java 9有一些变化)。
在Java 8中运行的更好的解决方案(我已经测试过):
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendPattern("[XXX][XX][X]")
.toFormatter();
System.out.println(OffsetDateTime.parse(ex, formatter));
模式XXX
,XX
和X
分别与+00:00
,+0000
和+00
匹配。我们需要按照从最长到最短的顺序尝试它们,以确保在所有情况下都解析所有文本。
答案 2 :(得分:1)
非常感谢你的所有投入!
我选择了jeedas建议的解串器,结合Ole V.V建议的格式化器(因为它更短)。
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时使用正确的格式:
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类路径中添加了一个@Configuraton
类,使其无需对数据类进行任何注释即可使用:
@Configuration
open class JacksonConfig {
@Bean
open fun jacksonCustomizer() = Jackson2ObjectMapperBuilderCustomizer {
it.deserializers(DefensiveIsoOffsetDateTimeDeserializer())
it.serializers(OffsetDateTimeSerializer())
}
}