序列化/反序列化Java 8 Java。时间与杰克逊JSON映射器

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

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

514229 次浏览

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

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

随机事实:如果我在类上面嵌套并且不使它们静态,错误消息是奇怪的: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported < / p >

这个maven依赖将解决你的问题:

<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.4</version>
</dependency>
我一直挣扎的一件事是,在反序列化期间,ZonedDateTime时区被更改为GMT。 结果,Jackson默认用上下文中的一个替换它。 要保留区域1,必须禁用此“功能”

Jackson2ObjectMapperBuilder.json()
.featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
如果您正在使用fastxml的ObjectMapper类, 默认情况下,ObjectMapper不理解LocalDateTime类,所以,你需要在你的gradle/maven中添加另一个依赖项:

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

现在你需要将这个库提供的数据类型支持注册到你的objectmapper对象中,这可以通过以下方式完成:

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

现在,在你的jsonString中,你可以很容易地把你的java。LocalDateTime字段如下所示:

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

通过这样做,你的Json文件到Java对象的转换将工作良好,你可以通过以下方式读取文件:

objectMapper.readValue(jsonString, new TypeReference<List<User>>() {
});
我在使用春天的引导时遇到了类似的问题。 使用Spring启动1.5.1。释放我所要做的就是添加依赖:

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

如果你正在使用Jersey,那么你需要像其他人建议的那样添加Maven依赖项(jackson- dattype -jsr310),并像这样注册你的对象映射器实例:

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

当在你的资源中注册Jackson时,你需要像这样添加这个映射器:

final ResourceConfig rc = new ResourceConfig().packages("<your package>");
rc
.register(JacksonObjectMapper.class)
.register(JacksonJaxbJsonProvider.class);

如果由于某种原因不能使用jackson-modules-java8,则可以使用@JsonIgnore@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);
}
}

例子:

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

收益率

{"epochTime":1506432517242}
2017-09-26T13:28:37.242Z
这只是一个例子,如何使用它在一个单元测试,我黑调试这个问题。 关键成分是

  • mapper.registerModule(new JavaTimeModule());
  • <artifactId>jackson-datatype-jsr310</artifactId>的maven依赖项

代码:

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


}

我使用这个时间格式:"{birthDate": "2018-05-24T13:56:13Z}"来反序列化json到java.time.Instant(见截图)

enter image description here

你可以在你的application.yml文件中设置这个来解析即时时间,这是java8中的日期API:

spring.jackson.serialization.write-dates-as-timestamps=false

如果你正在使用Spring引导,并且在OffsetDateTime上有这个问题,那么需要使用上面@greperror回答的registerModules(5月28日16日13:04回答),但注意有一个区别。不需要添加提到的依赖项,因为我猜spring boot已经有它了。我有这个问题与春季启动,它为我工作,没有添加这个依赖。

对于那些使用Spring Boot 2.x的人

上面这些都不需要做——Java 8 LocalDateTime是开箱即用的序列化/反序列化。我必须在1年内完成以上所有工作。x,但是使用Boot 2。X,它可以无缝工作。

参见此引用JSON Java 8 LocalDateTime格式在Spring Boot

如果你考虑使用fastjson,你可以解决你的问题,注意版本

 <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>

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

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

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;

如果你有这个问题是因为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

如果你正在使用杰克逊序列化器,下面是一种使用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;
}
}

所有你需要知道的是在杰克逊文档 https://www.baeldung.com/jackson-serialize-dates < / p >

9号广告很快为我解决了这个问题。

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        <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();
不幸的是,这里提出的解决方案在我的环境中不起作用。 但是说实话,使用java8 time对象作为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;
}


}

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

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

我想为Spring的DurationStyle解析提供支持,在使用Jackson反序列化的自定义配置文件中的属性文件中支持,例如将20s序列化到Duration PT20S。为此,我在同样的ObjectMapper实例上注册了一个自定义反序列化器:

@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);
}
}
ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.registerModule(new JavaTimeModule());

这对我很有效

对于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 JSR的新版本中,例如,registerModule(new JSR310Module())已弃用,现在建议使用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;
}
}
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createTime;

这对我来说很有效。