352

How do I use Jackson JSON mapper with Java 8 LocalDateTime?

org.codehaus.jackson.map.JsonMappingException: Can not instantiate value of type [simple type, class java.time.LocalDateTime] from JSON String; no single-String constructor/factory method (through reference chain: MyDTO["field1"]->SubDTO["date"])

Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
Alexander Taylor
  • 16,574
  • 14
  • 62
  • 83
  • 4
    are you sure you want to map a LocalDateTime to JSon? As far as I know, JSon does not have a format for dates, although JavaScript uses ISO-8601. Problem is, LocalDateTime does not have a time zone... so, if you use JSON as medium to send date/time info, you might get in trouble if the client will interpret the lack of time zone as default UTC (or its own time zone). If that is what you want to do, of course it is fine. But just check if you have considered using ZonedDateTime instead – arcuri82 Oct 25 '16 at 20:13

25 Answers25

378

There's no need to use custom serializers/deserializers here. Use jackson-modules-java8's datetime module:

Datatype module to make Jackson recognize Java 8 Date & Time API data types (JSR-310).

This module adds support for quite a few classes:

  • Duration
  • Instant
  • LocalDateTime
  • LocalDate
  • LocalTime
  • MonthDay
  • OffsetDateTime
  • OffsetTime
  • Period
  • Year
  • YearMonth
  • ZonedDateTime
  • ZoneId
  • ZoneOffset
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • 97
    Oh, yes! I though this wasn't working because I didn't realize that one must do one of these customization to the mapper object: `registerModule(new JSR310Module())` or `findAndRegisterModules()`. See https://github.com/FasterXML/jackson-datatype-jsr310 and here is how to customize your mapper object if you use Spring framework: http://stackoverflow.com/questions/7854030/configurating-objectmapper-in-spring – Alexander Taylor Jan 15 '15 at 03:02
  • 17
    Doesn't work with the simplest one I tried with, `OffsetDateTime` `@Test` `public void testJacksonOffsetDateTimeDeserializer() throws IOException {` `ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());` `String json = "\"2015-10-20T11:00:00-8:30\"";` `mapper.readValue(json, OffsetDateTime.class);` `}` – Abhijit Sarkar Oct 20 '15 at 10:16
  • 9
    If you use recent Spring framework there is no need anymore to customize yourself, as well known Jackson modules such as this one are now automatically registered if they are detected on the classpath: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring#jackson-modules – vorburger Oct 30 '15 at 12:16
  • @Alexander Taylor = your answer seems to be the most helpful; please post it as a full answer. – Witold Kaczurba Mar 06 '17 at 05:57
  • 55
    JSR310Module is a bit outdated already, instead use JavaTimeModule – Jacob Eckel Mar 13 '17 at 15:32
  • @JacobEckel thanks for the heads-up. Do you mind suggesting an edit to this answer? – Matt Ball Mar 13 '17 at 16:43
  • 48
    @MattBall I would suggest adding that, in addition to using jackson-datatype-jsr310, you need to register the JavaTimeModule with your object mapper: `objectMapper.registerModule(new JavaTimeModule());`. Both on the serialization and the deserialization. – GrandAdmiral Apr 26 '17 at 21:14
  • @GrandAdmiral thanks for the tip. Don't hesitate to suggest an edit that you think will improve the answer! `:)` – Matt Ball Apr 26 '17 at 23:11
  • @deamon thanks! Feel free to edit the answer to improve it. – Matt Ball Sep 01 '17 at 01:16
  • 1
    @MattBall I've updated the links. The module is still called the same, but has a new home on GitHub. – deamon Sep 04 '17 at 06:42
  • Just for completion if you use Spring Boot and include the module from the accepted answer. Check out the following links for configuration: [look at chapter "With Spring Boot"] (https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring#jackson-modules) and also the [spring boot documentation] (https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-customize-the-jackson-objectmapper) – the hand of NOD Jul 19 '18 at 08:27
  • You have to use this dependency `jackson-datatype-jsr310` to work with date and time of java8. ` com.fasterxml.jackson.datatype jackson-datatype-jsr310 ${jackson.version} ` – privatejava Aug 23 '18 at 11:58
  • 1
    Don't forget to create your mapper like `ObjectMapper mapper = new ObjectMapper(); mapper.findAndRegisterModules();` – redsk Oct 08 '18 at 17:18
  • For `LocalDate`, `LocalTime`, `LocalDateTime`, and `OffsetTime` you get better results when disabling `WRITE_DATES_AS_TIMESTAMPS` – Kaarel Dec 05 '18 at 19:56
  • Refer the blog for Spring boot >> https://www.baeldung.com/spring-boot-formatting-json-dates – Anver Sadhat Apr 10 '19 at 10:32
  • 1
    datetime library java8 as opposed to legacy, with Jackson libs 2.x will fail when deserializing some dates as date-time properties in JSON are tried to be deserialized as OffsetDateTime instead of local date time if the format is YYYY-MM-DDTHH:MM:SS without the UTC data at the end of the string. So, do not thing that just because you register (or add in Jackson 2.x) some modules is going to work out of the box. I'm implementing my own deserializers for date-time for Jackson 2.x – Alex Oct 22 '20 at 20:00
  • registering JavaTimeModule solved the problem for me, thank you @GrandAdmiral – i89 Sep 28 '22 at 14:04
115

If you are using ObjectMapper class of fasterxml, by default ObjectMapper do not understand the LocalDateTime class, so, you need to add another dependency in your gradle/maven :

compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.3'

Now you need to register the datatype support offered by this library into you objectmapper object, this can be done by following :

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();

Now, in your jsonString, you can easily put your java.LocalDateTime field as follows :

{
    "user_id": 1,
    "score": 9,
    "date_time": "2016-05-28T17:39:44.937"
}

By doing all this, your Json file to Java object conversion will work fine, you can read the file by following :

objectMapper.readValue(jsonString, new TypeReference<List<User>>() {
            });
mkobit
  • 43,979
  • 12
  • 156
  • 150
greperror
  • 5,156
  • 3
  • 20
  • 29
  • 21
    `findAndRegisterModules()` was a critical piece that was missing for me when constructing an `ObjectMapper` – Xaero Degreaz Mar 24 '17 at 17:36
  • 2
    What about Instant? – powder366 Mar 19 '18 at 21:08
  • 3
    I also added this line: `objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);` – Janet Dec 03 '19 at 21:18
  • 2
    this is the complete code needed: ObjectMapper objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); – Sukh Dec 13 '19 at 18:27
  • I had to add this line to read timezone properly during deserialization phase: `mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);` – CᴴᴀZ Sep 09 '21 at 07:07
  • finally this is a perfect answer, spring boot already has the depedency, to load the data using object mapper needs this solution – anuj joshi Jan 10 '22 at 14:56
111

Update: Leaving this answer for historical reasons, but I don't recommend it. Please see the accepted answer above.

Tell Jackson to map using your custom [de]serialization classes:

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime ignoreUntil;

provide custom classes:

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
    @Override
    public void serialize(LocalDateTime arg0, JsonGenerator arg1, SerializerProvider arg2) throws IOException {
        arg1.writeString(arg0.toString());
    }
}

public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
    @Override
    public LocalDateTime deserialize(JsonParser arg0, DeserializationContext arg1) throws IOException {
        return LocalDateTime.parse(arg0.getText());
    }
}

random fact: if i nest above classes and don't make them static, the error message is weird: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported

dapc
  • 304
  • 3
  • 7
Alexander Taylor
  • 16,574
  • 14
  • 62
  • 83
  • 1
    as of this writing, the only advantage here over other answer is not depending on FasterXML, whatever that is. – Alexander Taylor Jan 15 '15 at 03:04
  • 4
    FasterXML **is** Jackson. http://fasterxml.com https://github.com/fasterxml/jackson – Matt Ball Jan 15 '15 at 03:15
  • 4
    oh i see. so many ancient pom entries in the project i'm working on. so no more org.codehaus, it's all com.fasterxml now. thanks! – Alexander Taylor Jan 15 '15 at 17:27
  • 2
    Also i was unable to use the built-in one because i needed to pass the formatter ISO_DATE_TIME. Not sure if it's possible to do that when using the JSR310Module. – isaac.hazan Mar 15 '15 at 12:11
  • Using this with Spring I had to create a bean of both `LocalDateTimeSerializer` and `LocalDateTimeDeserializer` – BenR Aug 07 '16 at 18:36
  • I needed to set these annotations to the getter and the setter instead of the attribute. – Ser Oct 10 '16 at 08:27
  • this works, and solves the problem asked in the question. However, it is risky, as the output is in a valid ISO-8601 format (where the lack of time zone would default to UTC). This might lead to problems in the client that will read this JSon, as it might wrongly assume this is a valid time value (which is not, as the time zone is undefined/wrong), unless this behavior is properly documented – arcuri82 Oct 25 '16 at 20:19
  • as of this writing, they both have to be static class if used as inner classes. and they both have to have a constructor with no arg. Whichever is broken, complains "no constructor having no arg". I guess this package is no longer well maintained or they pop up with another solution. – Tiina Dec 12 '17 at 06:05
59

This maven dependency will solve your problem:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.13.4</version>
</dependency>

One thing I've struggled is that for ZonedDateTime timezone being changed to GMT during deserialization. Turned out, that by default Jackson replaces it with one from context. To keep zone one must disable this 'feature'

Jackson2ObjectMapperBuilder.json()
    .featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
Yaroslav Boichuk
  • 1,763
  • 20
  • 31
iTake
  • 4,082
  • 3
  • 33
  • 26
  • 6
    Many thanks for DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE hint. – Andrej Urvantsev Jul 28 '16 at 12:22
  • 8
    As well as disabling `DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE` I also had to disable `SerializationFeature.WRITE_DATES_AS_TIMESTAMPS` for everything to start working as it should. – Matt Watson Mar 13 '17 at 15:10
36

all you need to know is in Jackson Documentation https://www.baeldung.com/jackson-serialize-dates

Ad.9 quick solved the problem for me.

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
Dave Kraczo
  • 748
  • 9
  • 9
  • 2
    Thanks Dave, you saved me from going crazy. Having `jackson-datatype-jsr310` dependency declared was not enough in my case, the three code lines you mentioned solved my problem. Thank you for posting the reference as well. – Francisco Acevedo Feb 14 '22 at 09:17
24

I had a similar problem while using Spring boot. With Spring boot 1.5.1.RELEASE all I had to do is to add dependency:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
mkobit
  • 43,979
  • 12
  • 156
  • 150
Witold Kaczurba
  • 9,845
  • 3
  • 58
  • 67
  • Thank you Witold. Hi everybody, note also @DaveKraczo post, I needed to apply both this and Dave's suggestion to solve my case. Thank you both. – Francisco Acevedo Feb 14 '22 at 09:20
14

In the newer version of Jackson JSR, e.g., the registerModule(new JSR310Module()) is deprecated, now the suggested one is JavaTimeModule

import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonFactory {
    private static ObjectMapper objectMapper = null;
    public static ObjectMapper getObjectMapper() {
        if (objectMapper == null) {
            objectMapper = new ObjectMapper();
            objectMapper.registerModule(new JavaTimeModule());
        }
        return objectMapper;
    }
}
walker088
  • 141
  • 1
  • 3
  • works flawlessly! add objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) – Gaurav Mar 23 '22 at 20:12
13

If you can't use jackson-modules-java8 for whatever reasons you can (de-)serialize the instant field as long using @JsonIgnore and @JsonGetter & @JsonSetter:

public class MyBean {

    private Instant time = Instant.now();

    @JsonIgnore
    public Instant getTime() {
        return this.time;
    }

    public void setTime(Instant time) {
        this.time = time;
    }

    @JsonGetter
    private long getEpochTime() {
        return this.time.toEpochMilli();
    }

    @JsonSetter
    private void setEpochTime(long time) {
        this.time = Instant.ofEpochMilli(time);
    }
}

Example:

@Test
public void testJsonTime() throws Exception {
    String json = new ObjectMapper().writeValueAsString(new MyBean());
    System.out.println(json);
    MyBean myBean = new ObjectMapper().readValue(json, MyBean.class);
    System.out.println(myBean.getTime());
}

yields

{"epochTime":1506432517242}
2017-09-26T13:28:37.242Z
Udo
  • 2,300
  • 1
  • 23
  • 27
  • This is a very clean method and allows users of your class to use whatever way they want. – Ashhar Hasan Feb 25 '19 at 16:00
  • How will this deserialize `timezone` information? – CᴴᴀZ Sep 09 '21 at 07:10
  • `java.time.Instant` has no time zone - it's "An instantaneous point on the time-line." If you want to (de-)serialize `TimeZone` however you can do it in the same way as above by simply serializing `TimeZone.getID()` (annotated as `@JsonGetter`). For deserialization you can use `TimeZone.getTimeZone(timeZoneId)` (annotated as `@JsonSetter`). The methods `get/setTimeZone()` are - again - annotated as `@JsonIgnore` – Udo Sep 10 '21 at 08:18
10

If you are using Jersey then you need to add the Maven dependency (jackson-datatype-jsr310) as the others suggested and register your object mapper instance like so:

@Provider
public class JacksonObjectMapper implements ContextResolver<ObjectMapper> {

  final ObjectMapper defaultObjectMapper;

  public JacksonObjectMapper() {
    defaultObjectMapper = createDefaultMapper();
  }

  @Override
  public ObjectMapper getContext(Class<?> type) {
    return defaultObjectMapper;
  }

  private static ObjectMapper createDefaultMapper() {
    final ObjectMapper mapper = new ObjectMapper();    
    mapper.registerModule(new JavaTimeModule());
    return mapper;
  }
}

When registering Jackson in your resources, you need to add this mapper like so:

final ResourceConfig rc = new ResourceConfig().packages("<your package>");
rc
  .register(JacksonObjectMapper.class)
  .register(JacksonJaxbJsonProvider.class);
mkobit
  • 43,979
  • 12
  • 156
  • 150
Sul Aga
  • 6,142
  • 5
  • 25
  • 37
7

If you are using Jackson Serializer, here is a way to use the date modules:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.apache.kafka.common.serialization.Serializer;

public class JacksonSerializer<T> implements Serializer<T> {

    private final ObjectMapper mapper = new ObjectMapper()
            .registerModule(new ParameterNamesModule())
            .registerModule(new Jdk8Module())
            .registerModule(new JavaTimeModule());

    @Override
    public byte[] serialize(String s, T object) {
        try {
            return mapper.writeValueAsBytes(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}
Eduardo Briguenti Vieira
  • 4,351
  • 3
  • 37
  • 49
  • A snippet with `e.printStackTrace()` shouldn't ever be posted IMO. Just wrap the exception and throw it. – Dariusz Aug 02 '22 at 11:26
6

This is just an example how to use it in a unit test that I hacked to debug this issue. The key ingredients are

  • mapper.registerModule(new JavaTimeModule());
  • maven dependency of <artifactId>jackson-datatype-jsr310</artifactId>

Code:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.IOException;
import java.io.Serializable;
import java.time.Instant;

class Mumu implements Serializable {
    private Instant from;
    private String text;

    Mumu(Instant from, String text) {
        this.from = from;
        this.text = text;
    }

    public Mumu() {
    }

    public Instant getFrom() {
        return from;
    }

    public String getText() {
        return text;
    }

    @Override
    public String toString() {
        return "Mumu{" +
                "from=" + from +
                ", text='" + text + '\'' +
                '}';
    }
}
public class Scratch {


    @Test
    public void JacksonInstant() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());

        Mumu before = new Mumu(Instant.now(), "before");
        String jsonInString = mapper.writeValueAsString(before);


        System.out.println("-- BEFORE --");
        System.out.println(before);
        System.out.println(jsonInString);

        Mumu after = mapper.readValue(jsonInString, Mumu.class);
        System.out.println("-- AFTER --");
        System.out.println(after);

        Assert.assertEquals(after.toString(), before.toString());
    }

}
Filip Malczak
  • 3,124
  • 24
  • 44
Mircea Stanciu
  • 3,675
  • 3
  • 34
  • 37
5

If you're having this issue because of GraphQL Java Tools and trying to marshal an Java Instant from a date string, you need to setup your SchemaParser to use an ObjectMapper with certain configurations:

In your GraphQLSchemaBuilder class, inject ObjectMapper and add this modules:

        ObjectMapper objectMapper = 
    new ObjectMapper().registerModule(new JavaTimeModule())
            .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

and add it to the options:

final SchemaParserOptions options = SchemaParserOptions.newOptions()
            .objectMapperProvider(fieldDefinition -> objectMapper)
            .typeDefinitionFactory(new YourTypeDefinitionFactory())
            .build();

See https://github.com/graphql-java-kickstart/graphql-spring-boot/issues/32

Janac Meena
  • 3,203
  • 35
  • 32
4
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-parameter-names</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jdk8</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>

add these dependencies and enable these modules. that should help

    private static final ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
ifelse.codes
  • 2,289
  • 23
  • 21
4

For spring boot api :

@Configuration
public class JsonConfig {

    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper mapper = new ObjectMapper();

        mapper.registerModule(new ParameterNamesModule())
                .registerModule(new Jdk8Module())
                .registerModule(new JavaTimeModule());

        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);// will remove value properties
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        jsonConverter.setObjectMapper(mapper);
        return jsonConverter;
    }
}

import the following dependencies :

implementation 'com.fasterxml.jackson.core:jackson-core:2.13.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.0'
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Biju
  • 39
  • 1
3
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createTime;

This has worked for me.

2

I use this time format: "{birthDate": "2018-05-24T13:56:13Z}" to deserialize from json into java.time.Instant (see screenshot)

enter image description here

Taras Melnyk
  • 3,057
  • 3
  • 38
  • 34
2

You may set this in your application.yml file to resolve Instant time, which is Date API in java8:

spring.jackson.serialization.write-dates-as-timestamps=false
Andy Hoffner
  • 3,267
  • 2
  • 21
  • 22
Janki
  • 191
  • 1
  • 8
2

If you are using Spring boot and have this issue with the OffsetDateTime then need to use the registerModules as answered above by @greperror(answered May 28 '16 at 13:04) but note that there is one difference. The dependency mentioned doesn't need to be added as I am guessing that spring boot has it already. I was having this issue with Spring boot and it worked for me without adding this dependency.

VC2019
  • 427
  • 1
  • 5
  • 9
1

If any one having problem while using SpringBoot here is how I fixed the issue without adding new dependency.

In Spring 2.1.3 Jackson expects date string 2019-05-21T07:37:11.000 in this yyyy-MM-dd HH:mm:ss.SSS format to de-serialize in LocalDateTime. Make sure date string separates the date and time with T not with space. seconds (ss) and milliseconds(SSS) could be ommitted.

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;
Muhammad Usman
  • 863
  • 1
  • 11
  • 18
1

I wanted to provide support for Spring's DurationStyle parsing, supported in property files in my custom configuration files deserialized using Jackson, like serializing 20s to Duration PT20S. I did this by registering a custom deserializer on the ObjectMapper instance being used for the same:

@Bean("customConfigMapper")
public ObjectMapper customConfigMapper() {
    final ObjectMapper mapper = new ObjectMapper();
    final SimpleModule module = new SimpleModule();
    module.addDeserializer(Duration.class, new SpringDurationStyleDeserializer());
    mapper.registerModule(module);
    return mapper;
}

public static class SpringDurationStyleDeserializer extends JsonDeserializer<Duration> {
    @Override
    public Duration deserialize(JsonParser jsonParser, DeserializationContext __) throws IOException {
        return Optional.ofNullable(jsonParser.getText()).map(DurationStyle::detectAndParse).orElse(null);
    }
}
aksh1618
  • 2,245
  • 18
  • 37
1
ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.registerModule(new JavaTimeModule());

This worked for me

Dinesh
  • 11
  • 1
1

For those who are looking for a solution on version ES-8 and Spring Boot:3.0

Create a configuration file extending ElasticsearchConfiguration and override clientConfiguration and elasticsearchClient creation.

During elasticsearchClient creation inject your own objectMapper configured to use Java 8 time module, which will override the default objectMapper.

@Override
public ClientConfiguration clientConfiguration() {
    return ClientConfiguration.builder()
            .connectedTo(<Hostname> +":"+ <Port>)
            .usingSsl()
            .withBasicAuth(<Username>, <Password>)
            .build();
}


@Override
public ElasticsearchClient elasticsearchClient(RestClient restClient) {
    Assert.notNull(restClient, "restClient must not be null");

    //Create Java8 time module
    JavaTimeModule module = new JavaTimeModule();
    module.addSerializer(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DateFormat.date_time_no_millis.getPattern())));

    //Register the module with objectMapper
    ObjectMapper objectMapper=new ObjectMapper()
            .registerModule(module);

    //To convert datetime to ISO-8601
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);


    //Creating our own jsonpMapper 
    JsonpMapper jsonpMapper=new JacksonJsonpMapper(objectMapper);

    // Create the transport with a Jackson mapper
    ElasticsearchTransport transport = new RestClientTransport(
            restClient, jsonpMapper);

    // And create the API client
    return  new ElasticsearchClient(transport);
}

Maven dependency:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.14.0</version>
</dependency>
zenuve
  • 21
  • 4
0

For those who use Spring Boot 2.x

There is no need to do any of the above - Java 8 LocalDateTime is serialised/de-serialised out of the box. I had to do all of the above in 1.x, but with Boot 2.x, it works seamlessly.

See this reference too JSON Java 8 LocalDateTime format in Spring Boot

HopeKing
  • 3,317
  • 7
  • 39
  • 62
0

Unfortunately, the solution proposed here, didn't work in my environment. But to be honest, using java8 time objects as DTOs is not a very good idea after all.

I would recommend to create custom DTOs instead, and don't rely on the unstable libraries, which might break after next jdk release. This approach is also in accordance with good practices of anticorruption layer and adapter patterns.

Here is the example of the DTO:

public class ReportDTO implements Serializable {

    private YearMonthDTO yearMonth;

    public YearMonthDTO getYearMonth() {
        return yearMonth;
    }

    public void setYearMonth(final YearMonthDTO yearMonth) {
        this.yearMonth = yearMonth;
    }

    public void fromYearMonth(final YearMonth yearMonth) {
        this.yearMonth = new YearMonthDTO(yearMonth.getYear(), 
                              yearMonth.getMonthValue());
    }

}

public static class YearMonthDTO {

    private int year;

    private int monthValue;

    public YearMonthDTO() {

    }

    public YearMonthDTO(int year, int monthValue) {
        this.year = year;
        this.monthValue = monthValue;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonthValue() {
        return monthValue;
    }

    public void setMonthValue(int monthValue) {
        this.monthValue = monthValue;
    }

}

It of course depends on your situation, and the amount of work you would have to do with this solution. As any pattern, this solution is not applicable to all situations.

In any case, the current best answer doesn't seem to work anymore. I didn't try other solutions, but I decided not to rely on any libraries in my simple case.

Mykhailo Skliar
  • 1,242
  • 1
  • 8
  • 19
-4

If you consider using fastjson, you can solve your problem, note the version

 <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.56</version>
 </dependency>
取一个好的名字
  • 1,677
  • 14
  • 16