如何在Java 8 LocalDateTime中使用Jackson JSON映射器?
org.codehaus.jackson.map.JsonMappingException:无法从JSON字符串实例化[简单类型,类java.time.LocalDateTime]类型的值;没有单字符串构造函数/工厂方法(通过引用链:MyDTO [“ field1”]-> SubDTO [“ date”])
如何在Java 8 LocalDateTime中使用Jackson JSON映射器?
org.codehaus.jackson.map.JsonMappingException:无法从JSON字符串实例化[简单类型,类java.time.LocalDateTime]类型的值;没有单字符串构造函数/工厂方法(通过引用链:MyDTO [“ field1”]-> SubDTO [“ date”])
Answers:
此处无需使用自定义序列化器/解串器。使用jackson-modules-java8的datetime模块:
数据类型模块,使Jackson能够识别Java 8 Date&Time API数据类型(JSR-310)。
registerModule(new JSR310Module())
或findAndRegisterModules()
。见 github.com/FasterXML/jackson-datatype-jsr310这里是如果你使用Spring框架如何自定义映射对象:stackoverflow.com/questions/7854030/...
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);
}
objectMapper.registerModule(new JavaTimeModule());
。无论是序列化还是反序列化。
更新:由于历史原因,留下了这个答案,但我不建议这样做。请参阅上面接受的答案。
告诉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
LocalDateTimeSerializer
和LocalDateTimeDeserializer
如果您使用的是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>>() {
});
findAndRegisterModules()
是构建时失踪了我的关键部分ObjectMapper
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
这个Maven依赖将解决您的问题:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.6.5</version>
</dependency>
我一直在努力的一件事是,在反序列化期间将ZonedDateTime时区更改为GMT。原来,杰克逊默认使用上下文中的一个替换它。要保留区域一,必须禁用此“功能”
Jackson2ObjectMapperBuilder.json()
.featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE
我还必须禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
所有东西才能开始正常工作。
使用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
出于某种原因不能使用,可以long
使用@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>
码:
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());
}
}
对于那些使用Spring Boot 2.x的人
不需要执行上述任何操作-Java 8 LocalDateTime是开箱即用的序列化/反序列化。我必须在1.x中完成上述所有操作,但是在Boot 2.x中,它可以无缝运行。
如果由于GraphQL Java工具而遇到此问题,并尝试Instant
从日期字符串中封送Java ,则需要设置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序列化器,则可以使用以下方法来使用日期模块:
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;
}
}