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

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


当前回答

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

添加这些依赖项并启用这些模块。这应该会有帮助

    private static final ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();

其他回答

对于spring引导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;
    }
}

导入以下依赖项:

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'

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

告诉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'不支持

如果您因为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

不幸的是,这里提出的解决方案在我的环境中不起作用。 但说实话,使用java8时间对象作为dto毕竟不是一个很好的主意。

我建议创建自定义dto,不要依赖于不稳定的库,它可能在下次jdk发布后崩溃。这种方法也符合反腐败层和适配器模式的良好实践。

下面是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;
    }

}

当然,这取决于你的情况,以及你要用这个解决方案做的工作量。与任何模式一样,此解决方案并不适用于所有情况。

无论如何,目前最好的答案似乎不再适用了。我没有尝试其他解决方案,但我决定在这个简单的案例中不依赖任何库。

如果你正在使用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;
    }
}