Jackson JSONマッパーを使用したjava 8 java.timeのシリアライズ/デシリアライズ


221

Java 8 LocalDateTimeでJackson JSONマッパーを使用するにはどうすればよいですか?

org.codehaus.jackson.map.JsonMappingException:JSON文字列からタイプ[シンプルタイプ、クラスjava.time.LocalDateTime]の値をインスタンス化できません。単一の文字列コンストラクタ/ファクトリメソッドはありません(参照チェーンを通じて:MyDTO ["field1"]-> SubDTO ["date"])


4
LocalDateTimeをJSonにマップしてもよろしいですか?私の知る限り、JavaScriptはISO-8601を使用していますが、JSonには日付の形式がありません。問題は、LocalDateTimeにはタイムゾーンがないことです。そのため、JSONを媒体として日付/時刻情報を送信する場合、タイムゾーンがないことをデフォルトのUTC(または独自のUTC)として解釈すると、問題が発生する可能性があります。タイムゾーン)。それがあなたがやりたいことなら、もちろんそれで結構です。ただし、代わりに
ZonedDateTimeの

回答:


276

ここではカスタムシリアライザー/デシリアライザーを使用する必要はありません。jackson-modules-java8のdatetimeモジュールを使用します

JacksonにJava 8 Date&Time APIデータ型(JSR-310)を認識させるためのデータ型モジュール。

このモジュールはかなりの数のクラスのサポートを追加します:

  • 期間
  • インスタント
  • LocalDateTime
  • LocalDate
  • 現地時間
  • 月日
  • OffsetDateTime
  • オフセット時間
  • 限目
  • 年月
  • ZonedDateTime
  • ZoneId
  • ZoneOffset

59
ああ、そう!I私は1つのマッパーオブジェクトにこれらのカスタマイズのいずれかを実行しなければならないことを認識していなかったので、これは働いていなかったにもかかわらず: registerModule(new JSR310Module())またはfindAndRegisterModules()。参照 github.com/FasterXML/jackson-datatype-jsr310ここで、あなたはSpringフレームワークを使用する場合は、あなたのマッパーオブジェクトをカスタマイズする方法である:stackoverflow.com/questions/7854030/...
アレクサンダー・テイラー

10
私が試した最も単純なものでは動作しません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); }
Abhijit Sarkar

9
最近のSpringフレームワークを使用する場合、これ以上カスタマイズする必要はありません。このようなよく知られたJacksonモジュールは、クラスパスで検出された場合に自動的に登録されます。spring.io
vorburger '30 / 10/30

36
JSR310Moduleはすでに少し古いですが、代わりにJavaTimeModuleを使用してください
Jacob

17
@MattBallを追加することをお勧めします。jackson-datatype-jsr310を使用するだけでなく、JavaTimeModuleをオブジェクトマッパーに登録する必要がありますobjectMapper.registerModule(new JavaTimeModule());。シリアライゼーションとデシリアライゼーションの両方について。
GrandAdmiral 2017

76

更新:この答えは歴史的な理由で残していますが、お勧めしません。上記の回答をご覧ください。

ジャクソンにカスタムの[逆]シリアライゼーションクラスを使用してマッピングするように指示します。

@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


これを書いている時点で、他の答えに対する唯一の利点はFasterXMLに依存していません。
Alexander Taylor

4
FasterXML ジャクソンです。fastxml.com github.com/fasterxml/jackson
Matt Ball

4
ああなるほど。私が取り組んでいるプロジェクトの多くの古代pomエントリ。これでorg.codehausはなくなりました。すべてcom.fasterxmlになりました。ありがとう!
Alexander Taylor

2
また、フォーマッタISO_DATE_TIMEを渡す必要があるため、組み込みのものを使用できませんでした。JSR310Moduleを使用するときにそれが可能かどうかはわかりません。
isaac.hazan 2015年

春の私でこれを使用すると、両方のBeanを作成しなければならなかったLocalDateTimeSerializerLocalDateTimeDeserializer
BenR

53

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

4
findAndRegisterModules()ObjectMapper
Xaero Degreazを

2
インスタントはどうですか?
powder366

1
私もこの行を追加しました: objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Janet

これは必要な完全なコードです。ObjectMapper objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS、false);
khush

42

この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)

5
DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONEヒントに感謝します。
Andrei Urvantcev 2016

5
無効DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONEにするだけでなくSerializationFeature.WRITE_DATES_AS_TIMESTAMPS、すべてが正常に機能するように無効にする必要もありました。
Matt Watson

16

Spring bootの使用中に同様の問題が発生しました。Spring boot 1.5.1.RELEASEを使用すると、依存関係を追加するだけで済みます。

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

7

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

6

jackson-modules-java8何らかの理由で使用できない場合は、インスタントフィールドlong@JsonIgnoreand @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

これは非常にクリーンなメソッドであり、クラスのユーザーが好きな方法で使用できるようにします。
Ashhar Hasan

3

私はこの時間形式を使用します:"{birthDate": "2018-05-24T13:56:13Z}"jsonからjava.time.Instantにデシリアライズします(スクリーンショットを参照)

ここに画像の説明を入力してください


3

これは、この問題をデバッグするためにハッキングした単体テストでの使用方法の例にすぎません。主な成分は

  • mapper.registerModule(new JavaTimeModule());
  • のmaven依存 <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());
    }

}

2

Springブートを使用していて、OffsetDateTimeでこの問題が発生している場合は、@ greperrorによって上記で回答されたようにregisterModulesを使用する必要があります(5月28日16:13に回答)。ただし、違いが1つあることに注意してください。上記の依存関係は追加する必要はありません。これは、スプリングブートにすでに存在していると推測しているためです。私はSpring bootでこの問題を抱えていましたが、この依存関係を追加することなくうまくいきました。


1

これをapplication.ymlファイルに設定して、インスタント時間を解決することができます。これは、java8のDate APIです。

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

1

Spring Boot 2.xをお使いの方へ

上記のいずれも実行する必要はありません。Java8 LocalDateTimeはそのままの状態でシリアル化/非シリアル化されます。上記のすべてを1.xで行う必要がありましたが、Boot 2.xではシームレスに動作します。

この参照も参照してくださいSpring BootのJSON Java 8 LocalDateTime形式


1

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;

0

fastjsonの使用を検討している場合は、問題を解決できます。バージョンを確認してください

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

0

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を参照してください


0

あなたが使用している場合は、ジャクソンシリアライザを、ここでは、日付・モジュールを使用する方法は次のとおりです。

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;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.