レトロフィットGSONは日付をjson文字列からjava.util.dateにシリアル化します


83

REST呼び出しにRetrofitライブラリを使用しています。私が行ったことのほとんどはバターのようにスムーズでしたが、何らかの理由でJSONタイムスタンプ文字列をjava.util.Dateオブジェクトに変換する際に問題が発生しました。入ってくるJSONは次のようになります。

{
    "date": "2013-07-16",
    "created_at": "2013-07-16T22:52:36Z",
} 

RetrofitまたはGsonにこれらの文字列を変換するように指示するにはどうすればよいjava.util.Date objectsですか?


この質問の複製のように見えます。
davmrtl 2013

@Davmrtl:ここには2つの異なる日付形式があるため、同じではありません。したがって、別のアプローチが必要です。
giampaolo 2013

回答:


166
Gson gson = new GsonBuilder()
    .setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
    .create();

RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint(API_BASE_URL)
    .setConverter(new GsonConverter.create(gson))
    .build();

またはKotlinの同等物:

val gson = GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create()
RestAdapter restAdapter = Retrofit.Builder()
    .baseUrl(API_BASE_URL)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()
    .create(T::class.java)

カスタマイズしたGsonパーサーを後付けに設定できます。詳細はこちら:Retrofit Webサイト

Ondrejuの応答を見て、これをretrofit2に実装する方法を確認してください。


8
OPや他の読者にとってより役立つように、コードを簡単に説明してください。
Mohit Jain 2014年

すべてのリクエストに対してこれを行うにはどうすればよいですか?だから私にそれを追加しServiceGeneratorますか?
Zapnologica 2015年

4
これは実際にはロケールを適用しません。私のロケール2016-02-11T13:42:14.401Z用の日付オブジェクトに逆シリアル化され13:42:14ます。
Alireza Mirian 2016

私はこれを試しました、ダウンロードされたアイテムは今働いているようです。しかし、Jsonオブジェクトをサーバーに投稿すると。その後、タイムゾーンが削除されますか?
Zapnologica 2017年

このソリューションを実装した後、理由もなくアプリのクラッシュの問題を検出するために多くの時間を費やしました。私はコピーしてDETE形式を貼り付けるときに何かがから引用符を変更「T」と「T」 -慎重時計-異なるがあります!
LukaszTaraszka 2017

86

@gderacoの回答がretrofit2.0に更新されました:

Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();

Retrofit retrofitAdapter = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();

2
日付形式をコピーするときは引用符に注意してください-問題がありました!
LukaszTaraszka 2017

14

これが私がそれをした方法です:

Dateを拡張するDateTimeクラスを作成してから、カスタムデシリアライザーを記述します。

public class DateTime extends java.util.Date {

    public DateTime(long readLong) {
        super(readLong);
    }

    public DateTime(Date date) {
        super(date.getTime());
    }       
}

次に、DateコンバーターとDateTimeコンバーターの両方を登録するデシリアライザー部分について説明します。

public static Gson gsonWithDate(){
    final GsonBuilder builder = new GsonBuilder();

    builder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {  

        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");  
        @Override  
        public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {  
            try {  
                return df.parse(json.getAsString());  
            } catch (final java.text.ParseException e) {  
                e.printStackTrace();  
                return null;  
            }  
        }
    });

    builder.registerTypeAdapter(DateTime.class, new JsonDeserializer<DateTime>() {  

        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
        @Override  
        public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {  
            try {  
                return new DateTime(df.parse(json.getAsString()));  
            } catch (final java.text.ParseException e) {
                e.printStackTrace();  
                return null;  
            }  
        }
    });

    return builder.create();
}

また、RestAdapterを作成するときは、次のようにします。

new RestAdapter.Builder().setConverter(gsonWithDate());

Fooは次のようになります。

class Foo {
    Date date;
    DateTime created_at;
}

ありがとう!私はからに変更する必要がありreturn df.parse(json.getAsString()); ました long timeStamp = Long.parseLong(json.getAsString()); return new java.util.Date(timeStamp);
Juan Saravia 2014年

すごい!ありがとうございました!
NickUnuchek 2017

7

Gsonは、カスタム形式で解析できない場合、1つの日時形式(ビルダーで指定された形式)とiso8601のみを処理できます。したがって、解決策は、カスタムデシリアライザーを作成することです。私が定義したあなたの問題を解決するために:

package stackoverflow.questions.q18473011;

import java.util.Date;

public class Foo {

    Date date;
    Date created_at;

    public Foo(Date date, Date created_at){
       this.date = date;
       this.created_at = created_at;
    }

    @Override
    public String toString() {
       return "Foo [date=" + date + ", created_at=" + created_at + "]";
    }

}

このデシリアライザーで:

package stackoverflow.questions.q18473011;

import java.lang.reflect.Type;
import java.text.*;
import java.util.Date;

import com.google.gson.*;

public class FooDeserializer implements JsonDeserializer<Foo> {

     public Foo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        String a = json.getAsJsonObject().get("date").getAsString();
        String b = json.getAsJsonObject().get("created_at").getAsString();

        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat sdfDateWithTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

        Date date, created;
        try {
           date = sdfDate.parse(a);
           created = sdfDateWithTime.parse(b);
        } catch (ParseException e) {
           throw new RuntimeException(e);
        }

        return new Foo(date, created);
    }

}

最後のステップはGson、適切なアダプターを使用してインスタンスを作成することです。

package stackoverflow.questions.q18473011;

import com.google.gson.*;

public class Question {

    /**
     * @param args
     */
    public static void main(String[] args) {
      String s = "{ \"date\": \"2013-07-16\",    \"created_at\": \"2013-07-16T22:52:36Z\"}";


      GsonBuilder builder = new GsonBuilder();
      builder.registerTypeAdapter(Foo.class, new FooDeserializer());

      Gson gson = builder.create();
      Foo myObject = gson.fromJson(s, Foo.class);

      System.out.println("Result: "+myObject);
    }

}

私の結果:

Result: Foo [date=Tue Jul 16 00:00:00 CEST 2013, created_at=Tue Jul 16 22:52:36 CEST 2013]

1
Foo内に15〜20個のフィールド(カスタムオブジェクトを含む)がある場合、手動で逆シリアル化する必要があります。それはGsonの目的を殺します。
Farid 2018

1

文字通り、作成しているクラスに「created_at」という名前のDateオブジェクトがすでにある場合は、次のように簡単です。

Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").create();
YourObject parsedObject1 = gson.fromJson(JsonStringYouGotSomehow, YourObject.class);

これで完了です。複雑なオーバーライドは必要ありません。


「これで完了です。複雑なオーバーライドは必要ありません」の前に、まずRetrofitで試してみるべきでした。
Farid 2018

1

次のように2つの新しいクラスを定義できます。

import java.util.Date;

public class MyDate extends Date {
}

そして

import java.util.Date;

public class CreatedAtDate extends Date {
}

POJOは次のようになります。

import MyDate;
import CreatedAtDate;

public class Foo {
    MyDate date;
    CreatedAtDate created_at;
}

最後に、カスタムデシリアライザーを設定します。

public class MyDateDeserializer implements JsonDeserializer<Date> {

    public static final SimpleDateFormat sServerDateDateFormat = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public MyDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
         if (json != null) {
            final String jsonString = json.getAsString();
            try {
                return (MyDate) sServerDateDateFormat.parse(jsonString);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

そして

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(MyDate.class, new MyDateDeserializer());

0

これは尋ねられた質問に直接答えるものではありませんが、コーダーが問題を解決する方法について完全な選択の自由を持っている場合、私の意見では「最先端」です。

まず第一に、java.util.Dateを使用することは最善の解決策ではありません。理由は、これらのクラスが一部のコーナーケースで理想的な動作をしなかったため、Java Instantクラスなどによってスーパーシードされた場合、このSOの質問でBasil Bourqueの回答を確認してください:APIレベルが16以下のKotlinでDateオブジェクトを作成する

そこで、AndroidでThreeTenABPのInstantクラスを使用し、Kotlinを使用しました。

val gson = GsonBuilder().registerTypeAdapter(Instant::class.java,
    JsonDeserializer<Instant> { json: JsonElement, _: Type?, _: JsonDeserializationContext? ->
        ZonedDateTime.parse(
            json.asJsonPrimitive.asString
        ).toInstant()
    }
).create()

val retrofit = Retrofit.Builder()
    .baseUrl(baseUrl)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.