web-dev-qa-db-ja.com

日付けフォーマットJSON Jacksonへのマッピング

私はこのようなAPIから来る日付フォーマットを持っています:

"start_time": "2015-10-1 3:00 PM GMT+1:00"

これはYYYY-DD-MMです。HH:MM午前/午後のGMTタイムスタンプ。この値をPOJOのDate変数にマッピングしています。明らかに、その表示変換エラーです。

私は2つのことを知りたいのですが:

  1. Jacksonで変換を実行するために必要なフォーマットは何ですか? Dateはこれに適したフィールドタイプですか?
  2. 一般に、変数がJacksonによってObjectメンバーにマップされる前にそれらを処理する方法はありますか?フォーマットの変更、計算など.
121
Kevin Rave

Jacksonで変換を実行するために必要なフォーマットは何ですか? Dateはこれに適したフィールドタイプですか?

Dateはこのためのファインフィールド型です。 ObjectMapper.setDateFormatを使用すると、JSONを非常に簡単に解析可能にすることができます。

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
myObjectMapper.setDateFormat(df);

一般に、変数がJacksonによってObjectメンバーにマップされる前にそれらを処理する方法はありますか?フォーマットの変更、計算など.

はい。カスタムのJsonDeserializerを実装するなど、いくつかの選択肢があります。 JsonDeserializer<Date>を拡張します。 これ は良いスタートです。

101
pb2q

Jackson v2.0以降、@ JsonFormatアノテーションを直接Objectメンバーに使用することができます。

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z")
private Date date;
280

もちろん自動化された方法がありますはシリアライゼーションとデシリアライゼーションと呼ばれ、pb2qでも言及されているように特定のアノテーション(@JsonSerialize@JsonDeserialize)で定義できます。 。

Java.util.DateとJava.util.Calendar ...、そしておそらくJodaTimeも使えます。

@JsonFormatアノテーションは私が望んでいたようには動作しませんでした(これには別の値にタイムゾーンを調整したがあります)デシリアライゼーション中(シリアル化は完璧に動作しました):

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "CET")

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Europe/Budapest")

予測結果が欲しい場合は@JsonFormatアノテーションの代わりにカスタムシリアライザとカスタムデシリアライザを使用する必要があります。私はここで本当に良いチュートリアルと解決策を見つけました http://www.baeldung.com/jackson-serialize-dates

Dateフィールドの例がありますが、私はCalendarフィールドに必要なのでこれが私の実装です

シリアライザクラス:

public class CustomCalendarSerializer extends JsonSerializer<Calendar> {

    public static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    public static final Locale LOCALE_HUNGARIAN = new Locale("hu", "HU");
    public static final TimeZone LOCAL_TIME_ZONE = TimeZone.getTimeZone("Europe/Budapest");

    @Override
    public void serialize(Calendar value, JsonGenerator gen, SerializerProvider arg2)
            throws IOException, JsonProcessingException {
        if (value == null) {
            gen.writeNull();
        } else {
            gen.writeString(FORMATTER.format(value.getTime()));
        }
    }
}

デシリアライザクラス:

public class CustomCalendarDeserializer extends JsonDeserializer<Calendar> {

    @Override
    public Calendar deserialize(JsonParser jsonparser, DeserializationContext context)
            throws IOException, JsonProcessingException {
        String dateAsString = jsonparser.getText();
        try {
            Date date = CustomCalendarSerializer.FORMATTER.parse(dateAsString);
            Calendar calendar = Calendar.getInstance(
                CustomCalendarSerializer.LOCAL_TIME_ZONE, 
                CustomCalendarSerializer.LOCALE_HUNGARIAN
            );
            calendar.setTime(date);
            return calendar;
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

上記のクラスのおよび使用方法

public class CalendarEntry {

    @JsonSerialize(using = CustomCalendarSerializer.class)
    @JsonDeserialize(using = CustomCalendarDeserializer.class)
    private Calendar calendar;

    // ... additional things ...
}

この実装を使用して、シリアライゼーションおよびデシリアライゼーションプロセスを連続して実行すると、Originの値が連続して発生します。

@JsonFormatアノテーションのみを使用して逆シリアル化すると異なる結果が得られます私はあなたがアノテーションパラメータで変更することができないものを内部のタイムゾーンのデフォルト設定のために考えます(Jacksonライブラリ2.5.3と2.6.3の私の経験でした)バージョンも)。

44
Miklos Krivan

RFC3339 datetimeフォーマットのスプリングブートアプリケーションの完全な例

package bj.demo;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;

import Java.text.SimpleDateFormat;

/**
 * Created by [email protected] at 2018/5/4 10:22
 */
@SpringBootApplication
public class BarApp implements ApplicationListener<ApplicationReadyEvent> {

    public static void main(String[] args) {
        SpringApplication.run(BarApp.class, args);
    }

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"));
    }
}
3
BaiJiFeiLong

@ miklov-krivenの非常に有益な回答を基にして、これら2つの追加の考慮点が誰かに役立つことを証明することを願っています。

(1)同じクラスの中で静的な内部クラスとしてシリアライザとデシリアライザを含めるのはいい考えだと思います。 NB、SimpleDateFormatのスレッドセーフのためにThreadLocalを使用します。

public class DateConverter {

    private static final ThreadLocal<SimpleDateFormat> sdf = 
        ThreadLocal.<SimpleDateFormat>withInitial(
                () -> {return new SimpleDateFormat("yyyy-MM-dd HH:mm a z");});

    public static class Serialize extends JsonSerializer<Date> {
        @Override
        public void serialize(Date value, JsonGenerator jgen SerializerProvider provider) throws Exception {
            if (value == null) {
                jgen.writeNull();
            }
            else {
                jgen.writeString(sdf.get().format(value));
            }
        }
    }

    public static class Deserialize extends JsonDeserializer<Date> {
        @Overrride
        public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws Exception {
            String dateAsString = jp.getText();
            try {
                if (Strings.isNullOrEmpty(dateAsString)) {
                    return null;
                }
                else {
                    return new Date(sdf.get().parse(dateAsString).getTime());
                }
            }
            catch (ParseException pe) {
                throw new RuntimeException(pe);
            }
        }
    }
}

(2)個々のクラスメンバーごとに@JsonSerializeおよび@JsonDeserializeアノテーションを使用する代わりに、アプリケーションレベルでカスタムシリアル化を適用することでJacksonのデフォルトシリアル化をオーバーライドすることもできます。つまり、Date型のクラスメンバーはすべてJacksonによってシリアル化されます各フィールドに明示的な注釈を付けずにこのカスタムシリアル化を使用します。たとえばSpring Bootを使用している場合は、次のようにします。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public Module customModule() {
        SimpleModule module = new SimpleModule();
        module.addSerializer(Date.class, new DateConverter.Serialize());
        module.addDeserializer(Date.class, new Dateconverter.Deserialize());
        return module;
    }
}
1
Stuart

Java.sql.Dateのカスタム日付フォーマットの使用に問題がある場合は、これが最も簡単な解決策です。

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Java.sql.Date.class, new DateSerializer());
mapper.registerModule(module);

(このような答えは私に多くのトラブルを救った: https://stackoverflow.com/a/35212795/3149048

JacksonはデフォルトでSqlDateSerializerをJava.sql.Dateに使用していますが、現在、このシリアライザはdateformatを考慮に入れていません。この問題を参照してください。 https://github.com/FasterXML/ jackson-databind/issues/1407 。回避策は、コード例に示すように、Java.sql.Dateに別のシリアライザを登録することです。

1
Stephanie

他の回答で説明されているようにSimpleDateFormatを設定することは、私が想定しているJava.util.Dateに対してのみ有効であることを指摘したいと思います。しかしJava.sql.Dateの場合、フォーマッタは機能しません。私の場合、シリアル化する必要があるモデルでは実際にはフィールドがJava.utl.Dateであったが実際のオブジェクトがJava.sql.Dateになってしまったため、フォーマッターが機能しなかった理由はあまり明確ではありませんでした。これは可能です

public class Java.sql extends Java.util.Date

だからこれは実際に有効です

Java.util.Date date = new Java.sql.Date(1542381115815L);

そのため、Dateフィールドが正しくフォーマットされていない理由がわからない場合は、そのオブジェクトが本当にJava.util.Dateであることを確認してください。

ここで は、なぜJava.sql.Dateの扱いが追加されないのかも言及されています。

これはそれから変化を変えることになるでしょう、そして私はそれが保証されるとは思わない。私たちがゼロから始めたのなら、私はその変化に賛成するでしょうが、それほど多くはありません。

1
LazerBass

私のために働いています。 SpringBoot.

 import com.alibaba.fastjson.annotation.JSONField;

 @JSONField(format = "yyyy-MM-dd HH:mm:ss")  
 private Date createTime;

出力:

{ 
   "createTime": "2019-06-14 13:07:21"
}
0
anson