我如何使用杰克逊JSON映射与Java 8 LocalDateTime?

jsonmappingexception:不能实例化类型[简单类型,java.time类]的值。LocalDateTime] from JSON字符串;没有单字符串构造函数/工厂方法(通过引用链:MyDTO["field1"]->SubDTO["date"])


当前回答

如果有人在使用SpringBoot时遇到问题,这里是我在不添加新的依赖项的情况下修复问题的方法。

在Spring 2.1.3中,Jackson期望在此yyyy-MM-dd HH:mm:ss中出现日期字符串2019-05-21T07:37:11.000。在LocalDateTime中反序列化SSS格式。确保日期字符串用T分隔日期和时间,而不是用空格。秒(ss)和毫秒(SSS)可以省略。

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;

其他回答

更新:由于历史原因,留下这个答案,但我不建议这样做。请参阅上面接受的答案。

告诉Jackson使用自定义的[反]序列化类进行映射:

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

提供自定义类:

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());
    }
}

随机事实:如果我在类上面嵌套并且不使它们静态,错误消息是奇怪的: httpmediatypenotsupportedexception:内容类型'application/json;charset=UTF-8'不支持

对于那些正在寻找ES-8和Spring Boot:3.0版本的解决方案的人

创建一个扩展ElasticsearchConfiguration的配置文件,并覆盖clientConfiguration和elasticsearchClient的创建。

在创建elasticsearchClient期间,注入您自己的配置为使用Java 8时间模块的objectMapper,它将覆盖默认的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>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.14.0</version>
</dependency>

这里不需要使用自定义序列化器/反序列化器。使用jackson-modules-java8的datetime模块:

数据类型模块,使Jackson识别Java 8日期和时间API数据类型(JSR-310)。

这个模块增加了对很多类的支持:

持续时间 即时 LocalDateTime LocalDate 本地时间 MonthDay OffsetDateTime OffsetTime 期 一年 YearMonth ZonedDateTime ZoneId ZoneOffset

如果您因为GraphQL Java工具而遇到此问题,并试图从日期字符串编组Java Instant,则需要设置SchemaParser以使用具有特定配置的ObjectMapper:

在GraphQLSchemaBuilder类中,注入ObjectMapper并添加以下模块:

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

并将其添加到选项中:

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

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

如果你正在使用Jackson Serializer,下面是一种使用date模块的方法:

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;
    }
}