New Java API Client throws JsonpMappingException

Hi,
I am currently migrating from High Rest API Client to Java APi Client and I got an error which I can not solve.

This is my code:

 @Override
    public Optional<MyClass> findById(String id, List<String> customerIds) throws IOException {
        var searchRequest = RequestsFactory.createDocumentByIdSearchRequest(indexName, id, customerIds);
        var searchResponse = elasticsearchJavaApiClient.search(searchRequest, MyClass.class);
        return documentTransformer.extractSingleDocumentFromSearchResponse(searchResponse);
    }

If I use ObjectNode.class instead of MyClass.class I get no error.
Previously I had no such error. The error occurs inside elasticsearchJavaApiClient.search()

co.elastic.clients.transport.TransportException: node: https://myDomain:9200/, status: 200, [es/search] Failed to decode response
...

Caused by: co.elastic.clients.json.JsonpMappingException: Error deserializing co.elastic.clients.elasticsearch.core.search.Hit: jakarta.json.JsonException: Jackson exception (JSON path: hits.hits[0]._source) (line no=1, column no=336, offset=-1)
	at co.elastic.clients.json.JsonpMappingException.from0(JsonpMappingException.java:134)
	at co.elastic.clients.json.JsonpMappingException.from(JsonpMappingException.java:121)
	at co.elastic.clients.json.ObjectDeserializer.deserialize(ObjectDeserializer.java:218)
	at co.elastic.clients.json.ObjectDeserializer.deserialize(ObjectDeserializer.java:148)
...
Caused by: jakarta.json.JsonException: Jackson exception
	at co.elastic.clients.json.jackson.JacksonUtils.convertException(JacksonUtils.java:39)
	at co.elastic.clients.json.jackson.JacksonJsonpMapper$JacksonValueParser.deserialize(JacksonJsonpMapper.java:142)
...
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.OffsetDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 335] (through reference chain: com.example.dto.MyModel["MyField"])

Hello! The crux of the issue seems to that MyField has a timezone offset and you don't have enough dependencies to make it work:

Did you install Jackson as mentioned here? It should come with the jsr310 library too.

Yes, here is my gradle:

// Elasticsearch Java API Client
    implementation "co.elastic.clients:elasticsearch-java:${elasticsearchJavaApiClientVersion}"
    implementation "com.fasterxml.jackson.core:jackson-databind:${jacksonDatabindCoreVersion}"

gradle.properties:

elasticsearchJavaApiClientVersion=8.13.2
jacksonDatabindCoreVersion=2.17.0

I have Elasticsearch 8.5.3. Should I also change the version to 8.13.2?

Hello! Jackson does not handle Java Time classes by default, you have to add the Time module to the mapper like so:

ObjectMapper mapper = JsonMapper.builder() 
   .addModule(new JavaTimeModule())
   .build();

and then use it to build the transport and subsequently the client.

1 Like

Thank you very much for the suggestion.

My mapper class was like:

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ObjectMapperUtil {

   private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

   public static ObjectMapper createMapper() {
       return new ObjectMapper()
           .setDateFormat(new SimpleDateFormat(DATE_FORMAT))
           .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)
           .registerModule(new JavaTimeModule());
   }

}

I changed it into this:

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ObjectMapperUtil {

    private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

    public static ObjectMapper createMapper() {
        return JsonMapper.builder()
            .addModule(new JavaTimeModule())
            .defaultDateFormat(new SimpleDateFormat(DATE_FORMAT))
            .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)
            .build();
    }
}

Still did not work. I have 2 OffsetDateTime typed fields in MyClass

Is the jackson error still the same?
We have an example project in the java client repo where the client is set up to serialize Time classes, you can try comparing your code with ours to see what could be missing (here is where the client is built).

1 Like

Yes it is the same error. I tried various combinations but did not work. Thank you for your suggestion. I really appreciate it.

I am going to compare it definitely.

@ltrotta trotta Yes your answer was correct!

I had written the mapper in the wrong place. Now I fixed it. So my final config:

        String[] elasticsearchHosts = elasticsearchEndpoints.split(",");
        HttpHost[] httpHosts = Arrays.stream(elasticsearchHosts)
                .map(HttpHost::create).toArray(HttpHost[]::new);

        // Create the low level client
        final RestClientBuilder restClientbuilder = RestClient.builder(httpHosts);

        // Set elasticsearch user credentials
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,
                new UsernamePasswordCredentials(username, password));

        restClientbuilder.setHttpClientConfigCallback(
                b -> b.setDefaultCredentialsProvider(credentialsProvider));

        RestClient restClient = restClientbuilder.build();

        ObjectMapper mapper = JsonMapper.builder()
                .addModule(new JavaTimeModule())
                .build();

        // Create the transport with a Jackson mapper which maps classes to json
        ElasticsearchTransport transport = new RestClientTransport(restClient,
                new JacksonJsonpMapper(mapper));

        return new ElasticsearchClient(transport);

But additionally I had to add lombok annotations to MyClass.class

@Getter
@ToString
@NoArgsConstructor

The reason was that I had two OffsetDateTime fields and could not be parsed. But it works now.

Thank you very much for the help.
Sincerely,
Murat

Glad we could help :grin: