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 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());
。シリアライゼーションとデシリアライゼーションの両方について。
更新:この答えは歴史的な理由で残していますが、お勧めしません。上記の回答をご覧ください。
ジャクソンにカスタムの[逆]シリアライゼーションクラスを使用してマッピングするように指示します。
@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 = 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>
私が苦労したことの1つは、逆シリアル化中に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の使用中に同様の問題が発生しました。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;
}
}
リソースにジャクソンを登録するときは、次のようにこのマッパーを追加する必要があります。
final ResourceConfig rc = new ResourceConfig().packages("<your package>");
rc
.register(JacksonObjectMapper.class)
.register(JacksonJaxbJsonProvider.class);
jackson-modules-java8
何らかの理由で使用できない場合は、インスタントフィールドlong
を@JsonIgnore
and @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をお使いの方へ
上記のいずれも実行する必要はありません。Java8 LocalDateTimeはそのままの状態でシリアル化/非シリアル化されます。上記のすべてを1.xで行う必要がありましたが、Boot 2.xではシームレスに動作します。
この参照も参照してくださいSpring BootのJSON Java 8 LocalDateTime形式
SpringBoot
ここで使用中に問題が発生した場合は、新しい依存関係を追加せずに問題を修正しました。
ではSpring 2.1.3
ジャクソン日付文字列を期待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ツールが原因でこの問題が発生し、Java Instant
を日付文字列からマーシャリングしようとしている場合は、特定の構成で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;
}
}