java - Jackson 使用 Java 8 反序列化 elasticsearch long as LocalDateTime
问题描述
我们有一个使用long
弹性搜索索引填充的日期字段。
字段映射为:
@Field(type = FieldType.Date)
@JsonFormat(shape = JsonFormat.Shape.NUMBER_INT)
private LocalDateTime created;
我使用Jackson
JavaTimeModule
andJdk8Module
和这个配置:
@Bean
public ElasticsearchOperations elasticsearchTemplate() {
return new ElasticsearchRestTemplate(client(), new CustomEntityMapper());
}
public static class CustomEntityMapper implements EntityMapper {
private final ObjectMapper objectMapper;
public CustomEntityMapper() {
//we use this so that Elasticsearch understands LocalDate and LocalDateTime objects
objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
//MUST be registered BEFORE calling findAndRegisterModules
.registerModule(new JavaTimeModule())
.registerModule(new Jdk8Module());
//only autodetect fields and ignore getters and setters for nonexistent fields when serializing/deserializing
objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
//load the other available modules as well
objectMapper.findAndRegisterModules();
}
@Override
public String mapToString(Object object) throws IOException {
return objectMapper.writeValueAsString(object);
}
@Override
public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
return objectMapper.readValue(source, clazz);
}
}
但是,当我尝试使用以下字段解析索引中的实体时:
"created" : 1563448935000
我收到一个错误:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_NUMBER_INT), expected VALUE_STRING: Expected array or string.
我认为,可以将 a 反序列化为long
日期,但我看不出我缺少什么。
如果我将它映射到Long
它当然可以工作,并且如果值存储为String
并且我们对其进行整形并正确格式化为@JsonFormat
. 但也有可能long->LocalDateTime
吗?
解决方案
LocalDateTime
要从epoch 的毫秒数开始构建,1970-01-01T00:00:00Z
我们需要一个时区。在2.9.9版本中,当毫秒出现时它会抛出异常:
不允许使用原始时间戳 (1563448935000)
java.time.LocalDateTime
:需要额外的信息,例如偏移量或时区(参见 Javadocs 类)
但是我们可以实现我们的反序列化器,它将尝试使用默认时区来执行此操作。示例实现可能如下所示:
class MillisOrLocalDateTimeDeserializer extends LocalDateTimeDeserializer {
public MillisOrLocalDateTimeDeserializer() {
super(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
@Override
public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
if (parser.hasToken(JsonToken.VALUE_NUMBER_INT)) {
long value = parser.getValueAsLong();
Instant instant = Instant.ofEpochMilli(value);
return LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
}
return super.deserialize(parser, context);
}
}
ZoneOffset.UTC
用来。在您的情况下,您可以提供您的或使用系统默认值。示例用法:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
public class JsonApp {
public static void main(String[] args) throws Exception {
JavaTimeModule javaTimeModule = new JavaTimeModule();
// override default
javaTimeModule.addDeserializer(LocalDateTime.class, new MillisOrLocalDateTimeDeserializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(javaTimeModule);
String json = "{\"created\":1563448935000}";
System.out.println(mapper.readValue(json, Created.class));
}
}
class Created {
private LocalDateTime created;
// getters, setters, toString
}
上面的代码打印:
Created{created=2019-07-18T11:22:15}
编辑:使用Jackson 2.9.0
,由于这个问题,提供的代码将不会被调用,因为findAndRegisterModules
在注册自定义模块后调用的代码将覆盖它。删除该调用将使整个场景工作。如果上述方法不适用于您的版本,则需要调试默认实现并找到原因。
推荐阅读
- r - R仅在特定列中对齐grob表中的文本
- reactjs - 重定向数千个已弃用的产品二维码
- node.js - 我应该在哪里关闭连接?
- javascript - 为什么我在返回函数中会出现此错误?
- javascript - bulkCreate 在 sequelize 中建立联结表数据用于建立多对多关联
- apache - 树莓派上的 apache 使用 system() 调用 bash 脚本不会写入文件
- node.js - 弹性搜索 match_all 过滤器仅在指定大小时返回所有文档
- c++ - 输入验证帮助 - 将额外输入添加到下一个变量
- java - 如何从地图中的标记获取纬度和经度
- javascript - 如何有效地实现 addEventListener?