serialize/deserialize java 8 java.time 与杰克逊 JSON 映射器
serialize/deserialize java 8 java.time with Jackson JSON mapper
如何将 Jackson JSON 映射器与 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"])
更新:由于历史原因留下这个答案,但我不推荐它。请参阅上面接受的答案。
告诉 Jackson 使用您自定义的 [de] 序列化进行映射 类:
@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
这里不需要使用自定义serializers/deserializers。使用 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:
- 时长
- 即时
- 本地日期时间
- 本地日期
- 当地时间
- 月日
- 偏移日期时间
- 偏移时间
- 期间
- 年
- 年月
- ZonedDateTime
- 区域编号
- 区域偏移
这个maven依赖可以解决你的问题:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.6.5</version>
</dependency>
我一直在努力的一件事是在反序列化过程中将 ZonedDateTime 时区更改为 GMT。
事实证明,默认情况下,杰克逊将其替换为上下文中的一个..
要保留第一区必须禁用此 'feature'
Jackson2ObjectMapperBuilder.json()
.featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
如果您使用的是 fasterxml 的 ObjectMapper class,
默认情况下,ObjectMapper 不理解 LocalDateTime class,因此,您需要在 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 boot 时遇到了类似的问题。
使用 Spring boot 1.5.1.RELEASE 我所要做的就是添加依赖项:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
如果您使用的是 Jersey,那么您需要按照其他人的建议添加 Maven 依赖项 (jackson-datatype-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
& [= 将即时字段(反)序列化为 long
17=]:
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(见屏幕截图)
您可以在 application.yml
文件中设置此项以解析即时时间,即 java8 中的日期 API:
spring.jackson.serialization.write-dates-as-timestamps=false
如果您正在使用 Spring 引导并且 OffsetDateTime 存在此问题,则需要使用上面由 @greperror 回答的 registerModules(2016 年 5 月 28 日在 13:04 回答)但请注意有一个区别。不需要添加提到的依赖项,因为我猜 spring boot 已经有了它。我在 Spring 启动时遇到了这个问题,它对我有用,但没有添加这种依赖性。
对于那些使用 Spring 启动 2.x
无需执行上述任何操作 - Java 8 LocalDateTime serialised/de-serialised 开箱即用。我必须在 1.x 中完成上述所有操作,但使用 Boot 2.x,它可以无缝运行。
也请参阅此参考资料
如果考虑使用fastjson,可以解决你的问题,注意版本
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
如果有人在使用 SpringBoot
时遇到问题,这是我在不添加新依赖项的情况下解决问题的方法。
In Spring 2.1.3
Jackson 期望 yyyy-MM-dd HH:mm:ss.SSS
格式中的日期字符串 2019-05-21T07:37:11.000
为 LocalDateTime
中的 de-serialize。确保日期字符串使用 T
而不是 space
分隔日期和时间。可以省略秒(ss
)和毫秒(SSS
)。
@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;
如果您因为 GraphQL Java 工具而遇到此问题并尝试从日期字符串编组 Java Instant
,则需要设置 SchemaParser 以使用具有特定配置的 ObjectMapper:
在您的 GraphQLSchemaBuilder class 中,注入 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,这里有一种使用日期模块的方法:
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;
}
}
所有你需要知道的都在 Jackson 文档中
https://www.baeldung.com/jackson-serialize-dates
Ad.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时间对象作为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
parsing 提供支持,在我使用 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;
}
}
如何将 Jackson JSON 映射器与 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"])
更新:由于历史原因留下这个答案,但我不推荐它。请参阅上面接受的答案。
告诉 Jackson 使用您自定义的 [de] 序列化进行映射 类:
@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
这里不需要使用自定义serializers/deserializers。使用 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:
- 时长
- 即时
- 本地日期时间
- 本地日期
- 当地时间
- 月日
- 偏移日期时间
- 偏移时间
- 期间
- 年
- 年月
- ZonedDateTime
- 区域编号
- 区域偏移
这个maven依赖可以解决你的问题:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.6.5</version>
</dependency>
我一直在努力的一件事是在反序列化过程中将 ZonedDateTime 时区更改为 GMT。 事实证明,默认情况下,杰克逊将其替换为上下文中的一个.. 要保留第一区必须禁用此 'feature'
Jackson2ObjectMapperBuilder.json()
.featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
如果您使用的是 fasterxml 的 ObjectMapper class, 默认情况下,ObjectMapper 不理解 LocalDateTime class,因此,您需要在 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 boot 时遇到了类似的问题。 使用 Spring boot 1.5.1.RELEASE 我所要做的就是添加依赖项:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
如果您使用的是 Jersey,那么您需要按照其他人的建议添加 Maven 依赖项 (jackson-datatype-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
& [= 将即时字段(反)序列化为 long
17=]:
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(见屏幕截图)
您可以在 application.yml
文件中设置此项以解析即时时间,即 java8 中的日期 API:
spring.jackson.serialization.write-dates-as-timestamps=false
如果您正在使用 Spring 引导并且 OffsetDateTime 存在此问题,则需要使用上面由 @greperror 回答的 registerModules(2016 年 5 月 28 日在 13:04 回答)但请注意有一个区别。不需要添加提到的依赖项,因为我猜 spring boot 已经有了它。我在 Spring 启动时遇到了这个问题,它对我有用,但没有添加这种依赖性。
对于那些使用 Spring 启动 2.x
无需执行上述任何操作 - Java 8 LocalDateTime serialised/de-serialised 开箱即用。我必须在 1.x 中完成上述所有操作,但使用 Boot 2.x,它可以无缝运行。
也请参阅此参考资料
如果考虑使用fastjson,可以解决你的问题,注意版本
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
如果有人在使用 SpringBoot
时遇到问题,这是我在不添加新依赖项的情况下解决问题的方法。
In Spring 2.1.3
Jackson 期望 yyyy-MM-dd HH:mm:ss.SSS
格式中的日期字符串 2019-05-21T07:37:11.000
为 LocalDateTime
中的 de-serialize。确保日期字符串使用 T
而不是 space
分隔日期和时间。可以省略秒(ss
)和毫秒(SSS
)。
@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;
如果您因为 GraphQL Java 工具而遇到此问题并尝试从日期字符串编组 Java Instant
,则需要设置 SchemaParser 以使用具有特定配置的 ObjectMapper:
在您的 GraphQLSchemaBuilder class 中,注入 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,这里有一种使用日期模块的方法:
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;
}
}
所有你需要知道的都在 Jackson 文档中 https://www.baeldung.com/jackson-serialize-dates
Ad.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时间对象作为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
parsing 提供支持,在我使用 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;
}
}