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"])
답변:
여기서는 사용자 정의 시리얼 라이저 / 디시리얼라이저를 사용할 필요가 없습니다. jackson-modules-java8의 datetime 모듈을 사용하십시오 .
Jackson이 Java 8 날짜 및 시간 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
fasterxml의 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로 변경된다는 것입니다. 기본적으로 jackson은이를 컨텍스트에서 하나로 대체합니다. 영역을 유지하려면이 '기능'을 비활성화해야합니다.
Jackson2ObjectMapperBuilder.json()
.featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE
하도록 비활성화 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
해야했습니다.
스프링 부트 를 사용하는 동안 비슷한 문제가 발생했습니다 . 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
어떤 이유로 든 사용할 수없는 경우 and & 를 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에서는 완벽하게 작동합니다.
스프링 부트의 JSON Java 8 LocalDateTime 형식 도 참조하십시오.
SpringBoot
여기에 사용하는 동안 문제가있는 사람 은 새로운 종속성을 추가하지 않고 문제를 해결 한 방법입니다.
Spring 2.1.3
Jackson 에서는 2019-05-21T07:37:11.000
이 yyyy-MM-dd HH:mm:ss.SSS
형식의 날짜 문자열이 에서 직렬화 해제 될 것으로 예상합니다 LocalDateTime
. 확인 날짜 문자열로 날짜와 시간을 구분 T
하지와를 space
. 초 ( ss
) 및 밀리 초 ( SSS
)는 생략 할 수 있습니다.
@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;
GraphQL Java 도구로 인해이 문제가 발생 Instant
하고 날짜 문자열에서 Java를 마샬링하려는 경우 특정 구성에서 ObjectMapper를 사용하도록 SchemaParser를 설정해야합니다.
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를 참조 하십시오.
당신이 사용하는 경우 잭슨 시리얼을 , 여기에 날짜 모듈을 사용하는 방법은 다음과 같습니다
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;
}
}