Jackson deserialize elasticsearch long as LocalDateTime with Java 8
To build LocalDateTime
from milliseconds from the epoch of 1970-01-01T00:00:00Z
we need a time zone. In version 2.9.9 it throws exception when milliseconds appears:
raw timestamp (1563448935000) not allowed for
java.time.LocalDateTime
: need additional information such as an
offset or time-zone (see class Javadocs)
But we can implement our deserialiser which will try to do this with default time zone. Example implementation could look like below:
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
is used. In your case you can provide yours or use system default. Example usage:
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
}
Above code prints:
Created{created=2019-07-18T11:22:15}
EDIT: Using Jackson 2.9.0
, because of this issue the code provided will not be invoked since findAndRegisterModules
which is called AFTER registering the customized module will override it. Removing that call will make the full scenario work. If above will not work for your version, you need to debug default implementation and find a reason.
Using Instant, LocalDateTime and ZonedDateTime with Spring Boot and ElasticSearch
Managed to get it to work with Spring Boot 2.1.4 and Spring Data Jest. Here is what I did:
Example domain object:
@Document(indexName = "datetest")
public class DateTest {
@Id
private String id;
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSSZZ", timezone = "UTC")
private Instant instant = Instant.now();
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
private ZonedDateTime zonedDateTime = ZonedDateTime.now();
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSS")
private LocalDateTime localDateTime = LocalDateTime.now();
// getters/setters
}The ElasticSearch/JEST config:
@Configuration
public class ESConfig {
@Bean
public EntityMapper getEntityMapper() {
return new CustomEntityMapper();
}
@Bean
@Primary
public ElasticsearchOperations elasticsearchTemplate(final JestClient jestClient,
final ElasticsearchConverter elasticsearchConverter,
final SimpleElasticsearchMappingContext simpleElasticsearchMappingContext, EntityMapper mapper) {
return new JestElasticsearchTemplate(jestClient, elasticsearchConverter,
new DefaultJestResultsMapper(simpleElasticsearchMappingContext, mapper));
}
public class CustomEntityMapper implements EntityMapper {
private final ObjectMapper objectMapper;
public CustomEntityMapper() {
objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
objectMapper.registerModule(new CustomGeoModule());
objectMapper.registerModule(new JavaTimeModule());
}
@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);
}
}
}The results in ElasticSearch:
Hope this helps.
Springframework Data Elasticsearch unable to create object of class java.time.ZonedDateTime
Change your pattern to use uuuu instead of yyyy; this is documented here, the change in Elasticsearch responsible for this: https://www.elastic.co/guide/en/elasticsearch/reference/current/migrate-to-java-time.html#java-time-migration-incompatible-date-formats
BTW, you don't need
httpHeaders.add("content-type", "application/json");
The @Json...
annotations are not needed for Spring Data Elasticsearch.
Related Topics
How to Make Program to Continue Running After Exception
Finding Repeated Words on a String and Counting the Repetitions
How to Find an Object in an Arraylist by Property
How to Display Auto-Configuration Report When Running a Spring Boot Application
How to Force a Method to Throw an Exception in Junit Testing
Error Starting Applicationcontext in Spring Boot App
How to Get Multiple Columns from Table Using Jpa
Error: Incompatible Types: List<Integer> Cannot Be Converted to Arraylist<Integer>
How to Import Two Classes With the Same Name in Different Packages
How to Get Spring Boot to Automatically Create Database Schema
How to Check Type of Variable in Java
How to Check If a Date Is Within a Certain Range
@Responsebody , Responseentity Spring Return Object as Json
How to Convert a One-Dimensional Array to Two-Dimensional Array in Java
Java 8 Streams - How to Merge Elements from the List With the Same Fields to One Element and Sum Up
How to Pass or Send Data from Recyclerview Adapter to Fragment