web-dev-qa-db-ja.com

jackson JSONマッパーを使用してJava 8 Java.timeをシリアライズ/デシリアライズする

Java 8 LocalDateTimeでJackson JSONマッパーを使用する方法

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

183

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

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

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

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

更新:歴史的な理由でこの回答を残していますが、私はそれをお勧めしません。上記の受け入れられた答えを見てください。

あなたのカスタム[de]シリアライゼーションクラスを使ってマッピングするように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

63

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

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

Spring boot を使っているときにも同じような問題がありました。 Spring boot 1.5.1では、依存関係を追加するだけです。

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

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);
6
Sul Aga

何らかの理由でjackson-modules-Java8を使用できない場合は、@JsonIgnoreおよび@JsonGetter@JsonSetterを使用して、インスタントフィールドをlongとしてシリアル化解除することができます。

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
5
Udo

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

enter image description here

2
Taras Melnyk

これは私がこの問題をデバッグするためにハックした単体テストでそれを使用する方法のほんの一例です。主な成分は

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

}
1
Mircea Stanciu

Spring Boot 2.x を使っている人のために

上記のいずれも行う必要はありません。Java8 LocalDateTimeは、箱から出してシリアル化/シリアル化解除されます。私は1.xで上記のすべてをしなければなりませんでした、しかしBoot 2.xで、それはシームレスに動きます。

この参照も参照してください Spring BootのJSON Java 8 LocalDateTimeフォーマット

1
HopeKing

SpringBootの使用中に問題が発生した場合は、新しい依存関係を追加せずに問題を修正します。

Spring 2.1.3では、Jacksonはこの2019-05-21T07:37:11.000形式の日付文字列yyyy-MM-dd HH:mm:ss.SSSLocalDateTimeで逆シリアル化されることを想定しています。日付文字列は、Tではなくspaceで日付と時刻を区切るようにしてください。秒(ss)とミリ秒(SSS)は省略できます。

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;
1
Muhammad Usman

インスタントタイムを解決するためにapplication.ymlファイルでこれを設定することができます。これはJava8のDate APIです。

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

Springブートを使用していて、OffsetDateTimeでこの問題が発生している場合は、上記の@greperrorによる回答(16年5月28日13時04分に回答)としてregisterModulesを使用する必要があります。私は春のブートはすでにそれを持っていると思いますので、言及されている依存関係を追加する必要はありません。私はSpringブートでこの問題を抱えていたので、この依存関係を追加しなくてもうまくいきました。

1
VC2019

あなたがfastjsonを使用することを検討している場合は、あなたの問題を解決することができます、バージョンに注意してください

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